Square CTF 2018
結局一人で8問解いて33位だった。問題はやさしめだったのでjyokenでも使えそう。
C1
うまいことデコード
C2
Common Exponent Attack
code:exploit.rb
require 'openssl'
def gcdext a, b
return 1, 0 if b == 0
q, r = a / b
x, y = gcdext b, r
end
c1 = 13981765388145083997703333682243956434148306954774120760845671024723583618341148528952063316653588928138430524040717841543528568326674293677228449651281422762216853098529425814740156575513620513245005576508982103360592761380293006244528169193632346512170599896471850340765607466109228426538780591853882736654
c2 = 79459949016924442856959059325390894723232586275925931898929445938338123216278271333902062872565058205136627757713051954083968874644581902371182266588247653857616029881453100387797111559677392017415298580136496204898016797180386402171968931958365160589774450964944023720256848731202333789801071962338635072065
e1 = 13
e2 = 15
n = 103109065902334620226101162008793963504256027939117020091876799039690801944735604259018655534860183205031069083254290258577291605287053538752280231959857465853228851714786887294961873006234153079187216285516823832102424110934062954272346111907571393964363630079343598511602013316604641904852018969178919051627
c1 = OpenSSL::BN.new c1
c2 = OpenSSL::BN.new c2
e1 = OpenSSL::BN.new e1
e2 = OpenSSL::BN.new e2
n = OpenSSL::BN.new n
x, y = gcdext e1, e2
m = x < 0 ? (c2**y).mod_mul(c1.mod_inverse(n)**(-x), n)
: (c1**x).mod_mul(c2.mod_inverse(n)**(-y), n)
C3
全探索+枝狩り
C4
code:text
at = Buffer.from( 178, 107, 103, 99, 15, 96, 247, 82, 65, 32, 146, 22, 197, 33, 169, 10 ... )
<Buffer b2 6b 67 63 0f 60 f7 52 41 20 92 16 c5 21 a9 0a>
d = gcm.decrypt(key, iv, ct, Buffer.from([]), at)
{ plaintext: <Buffer 43 4f 4e 46 49 44 45 4e 54 49 41 4c 0a 0a 54 6f 20 64 69 73 61 62 6c 65 20 43 34 2c 20 79 6f 75 20 77 69 6c 6c 20 6e 65 65 64 3a 0a 2d 20 36 20 62 69 ... 1112 more bytes>,auth_ok: false }
d.plaintext
<Buffer 43 4f 4e 46 49 44 45 4e 54 49 41 4c 0a 0a 54 6f 20 64 69 73 61 62 6c 65 20 43 34 2c 20 79 6f 75 20 77 69 6c 6c 20 6e 65 65 64 3a 0a 2d 20 36 20 62 69 ... 1112 more bytes>
d.plaintext.toString()
"CONFIDENTIAL\n\nTo disable C4, you will need:\n- 6 bits of Dragon Sumac\n- 1 nibble of Winter Spice\n- 1 byte of Drake Cardamom\n- 1 flag with value flag-e2f27bac480a7857de45\n- 2 diskfulls of Tundra Chives\n- 5 forks\n\nGrind the Dragon Sumac in a cup, making sure you don't break the cup as it's probably a delicate cup. Add a sprinkle of\nliquid ice to turn it into a cream-like paste, then add the Winter Spice, first almost everything, then the last tiny\nremnants.\n\nFill a pan with elemental water, add the mixture and cool it down with how cool you are, then bring the mixture\nto a boil. Let it cool down to the body temperature of a reptile before adding the Drake Cardamom and Tundra Chives,\nall at once of one, then half at a time of the other.\n\nBring everything back to a boil, turn of the heat, mix with the forks and let everything cool down. If you\ntouch the liquid and it burns you, it hasn't cooled down enough.\n\nWhisk the mixture heavily to aerate it. Stop when it's frothy.\n\nDrinking the potion will disable C4.\n\nnote: A small, but very cold amount is needed for the potion to be effective. Mixing it in a milkshake could work, but\nbe wary of brain freeze�"
C5
current passwordのvalueがFLAG
C6
0x80480c8 (int 0x80)から 0x80481caあたりまではコードではなくデータ
argc = 0x10
ebx = 0x25
16回
eax = 0x80481ca(ebx, ecx)
byte [esi] + 1 が0x30..0x69なら
DLが0x30未満の場合はアンダーフローして結局0x39より大きくなる
al = al * 10 + byte [esi] + 1
= メモリ上の数字をintとしてALに格納する
0x3039 → 39
ebx *= eax
256回 (i)
*(0x80480c9 + i) -= 1
ebx = 0x29a = 666 → FLAG? (call 0x80481e3)
16個の数字が引数
0x80480caからのオフセットを指定すると、それに対応するテーブルの値が掛けられる
数字が右にずれるごとにテーブルの値が1つ減る
0x29a / 0x25 = 18なので、9*2*1*1...でどうだろう
code:table
offset | 76, 90, 90,230,175, 48,148, 62,188,134,118, 35,132, 7,131,160
mem value | 9, 3, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, 10
delta | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
eax value | 9, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
aam 0x12
18進数?
aad 0xf6
246進数????
aadでSFが立てられる
先頭bitが1だから?
与える数字がでかすぎると立つっぽい(適当)
手で探ってみると36以上は立つ→35以下で収める必要がある
もう一度
なぜかoffset = 23, 20でSFが立ってしまったので適当に差し替えた
code:table
offset | 1, 15, 30, 17, 4, 16, 13, 11, 1, 8, 8, 12, 9, 7, 3, 6
mem value | 9, 2, 3, 4, 5, 6, 7, 8, 9, b, b, c, d, e, f, 10
delta | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
eax value | 9, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1
webサーバにクエリストリングで投げる flag-526f64696e0000666
angrとか使えないだろうか
C7
ソース参照
compute2は読まなくてOK
compute4で文字をシャッフルしていて、その結果の文字コードは単調増加でなければならない
つまり compute4(input) == "abcdef ... xyz"
シードは一定なので、シャッフルする前と後の文字の対応表を逆にたどってあげれば上のような文字列が生成できる
flag-705787f208e6eff63768ae166482125b
code:irb
irb(main):009:0> a = ("a".."z").to_a.join ""
=> "abcdefghijklmnopqrstuvwxyz"
irb(main):010:0> b = "hstqcjvmiozdyanwgrkxpeublf"
=> "hstqcjvmiozdyanwgrkxpeublf"
irb(main):011:0> x = Hash[a.chars.map.with_index {|c, i| [c, bi]}] => {"a"=>"h", "b"=>"s", "c"=>"t", "d"=>"q", "e"=>"c", "f"=>"j", "g"=>"v", "h"=>"m", "i"=>"i", "j"=>"o", "k"=>"z", "l"=>"d", "m"=>"y", "n"=>"a", "o"=>"n", "p"=>"w", "q"=>"g", "r"=>"r", "s"=>"k", "t"=>"x", "u"=>"p", "v"=>"e", "w"=>"u", "x"=>"b", "y"=>"l", "z"=>"f"}
irb(main):012:0> ans = a.chars.map { |c| x.key(c) }.join ""
=> "nxelvzqaifsyhojudrbcwgptmk"
C8
計算式が渡されるのでそれを即座に計算して答えをフォームで送信する
実際の文字はFFFFd l Eのようになっていて、それに対応するフォントを使うことで人の目には数字と演算子に見えるようになっている
はじめは手で対応付けようとして、Rubyでそれを支援するようなコードを書いた
おそすぎて無理だった
pittyiがOCRを使おうとしていたが無理だった
仕方がないのでCSSからfont-faceで指定されているttfを抜き取って解析してみるアプローチを取ることにした
opentype.jsを使うと思っていたよりずっと簡単だった
flag-a76013167fd4c04e3134
code:js
// ==UserScript==
// @name Solver
// @version 0.1
// @description try to take over the world!
// @author You
// @grant none
// ==/UserScript==
(function(){
'use strict';
const XTable = {
'510': '7',
'156': '0',
'520': '3',
'524': '8',
'438': '9',
'311': '1',
'487': '2',
'61': ')',
'109': '5',
'239': '(',
'444': 'x',
'438': '6',
'567': '4',
'76': '-',
'488': '+',
}
const detectChar = (font, idx) => {
const glyph = font.glyphs.get(idx)
switch (glyph.yMax) {
case 689: return (glyph.xMax == 561 ? '9' : '8')
case 696: return '7'
case 519: return '+'
case 660: return '0'
case 684: return '3'
case 690: return (glyph.xMax == 531 ? '5' : '4')
case 347: return '-'
case 481: return '*'
case 679: return '6'
case 673: return '1'
case 704: return '2'
default: return undefined
}
}
const onFontLoad = (err, font) => {
let st = document.querySelector('p').innerHTML;
// Replace brackets since they have the same x/y min/max.
st = st.replace(new RegExp(st0, 'g'), '(') // Replace characters in the source code with characters we actually see
const fMap = font.tables.cmap.glyphIndexMap;
for (let chrCode in fMap) {
let seenChar = detectChar(font, glyphIndex)
if (seenChar !== undefined) {
console.log(seenChar)
st = st.replace(new RegExp(String.fromCharCode(parseInt(chrCode, 0)), "g"), seenChar);
}
}
console.log(st)
// Submit
let ans = eval(st);
document.forms0.answer.value = ans; document.forms0.submit(); console.log("submitted");
};
let fontURL = document.styleSheets0.cssRules0.cssText.substring(64); fontURL = fontURL.substring(0, fontURL.length-5);
console.log(fontURL);
opentype.load(fontURL, onFontLoad)
})();
C9
PowerShellかと思ったらPostScriptだった
.ps1 vs .ps
LISPみたいで面白い
code:encrypt_str
buf1 = array(65)
buf2 = "4Loksa1t"
for (n = 0; n < 65; n++) {
}
code:test_str
buf_z = "800C...."
buf = array(118) // input?
fake_file = ASCIIHexDecode(buf_z)
fake_file2 = LZWDecode(fake_file)
while (ok == 118) {
}
面白いけど時間ないので撤退
C10
なんかエディタで絵文字表示できなくてキレたので撤退