MidnightsunCTF 2019 polyshell WriteUp

MidnightsunCTF 2019 pwnable

Posted by NextLine on August 7, 2019

MidnightsunCTF 2019 polyshell WriteUp

1. Intro

DEFCON의 doublethink와 비슷하게 하나의 쉘코드로 5개의 아키텍쳐에서 같은 로직을 실행하도록 짜야했다.

2. Solve

youngjoo@ubuntu1804 # ~$ nc polyshell-01.play.midnightsunctf.se 30000

Welcome to the polyglot challenge!
Your task is to create a shellcode that can run on the following architectures:
x86
x86-64
ARM
ARM64
MIPS-LE

The shellcode must run within 1 second(s) and may run for at most 100000 cycles.
The code must perform a syscall on each platform according to the following paramters:
Syscall number: 205
Argument 1: 39360
Argument 2: A pointer to the string "locate"

You submit your code as a hex encoded string of max 4096 characters (2048 bytes)

Your shellcode:

위와 같이 특정 argument를 가진 syscall을 5개의 아키텍쳐에서 실행해야 했다.

sc = '\xeb\x13\x00\x32'
sc += '\x0e\x00\x10\x12'
sc += '\x00\x00\x00\x32'
sc += '\x19\x00\x00\xea'
sc += '\x22\x00\x00\x14'

쉘코드에서 가장 핵심적인 내용은 위와 같은 jump문이다.

  • x86, x64
     0:   eb 13                   jmp    0x15
     2:   00 32                   add    BYTE PTR [edx],dh
     4:   0e                      push   cs
     5:   00 10                   add    BYTE PTR [eax],dl
     7:   12 00                   adc    al,BYTE PTR [eax]
     9:   00 00                   add    BYTE PTR [eax],al
     b:   32 19                   xor    bl,BYTE PTR [ecx]
    
  • mips
     0:   320013eb        andi    zero,s0,0x13eb
     4:   1210000e        beq     s0,s0,0x40
     8:   32000000        andi    zero,s0,0x0
     c:   ea000019        swc2    $0,25(s0)
    10:   14000022        bnez    zero,0x9c
    14:   90909090        lbu     s0,-28528(a0)
    
  • arm
     0:   320013eb        andcc   r1, r0, #-1409286141    ; 0xac000003
     4:   1210000e        andsne  r0, r0, #14
     8:   32000000        andcc   r0, r0, #0
     c:   ea000019        b       0x78
    10:   14000022        strne   r0, [r0], #-34  ; 0xffffffde
    14:   90909090        umullsls        r9, r0, r0, r0
    
  • arm64
     0:   320013eb        orr     w11, wzr, #0x1f
     4:   1210000e        and     w14, w0, #0x10000
     8:   32000000        orr     w0, w0, #0x1
     c:   ea000019        ands    x25, x0, x0
    10:   14000022        b       0x98
    14:   90909090        adrp    x16, 0xffffffff21210000
    18:   6c63be90        ldnp    d16, d15, [x20, #-456]
    

쉘코드를 짤 때 주의해야할 점은 mips 분기문에서 jump 인스트럭션 이후에 하나의 인스트럭션을 더 실행해야 점프한다는 점이다.

from pwn import *
s = remote('polyshell-01.play.midnightsunctf.se', 30000)
s.recvuntil('number: ')
syscall = int(s.recvline())
s.recvuntil('1: ')
argv1 = int(s.recvline())
s.recvuntil('string "')
argv2 = s.recvline()[:-2].ljust(8,'\x00')
s.info("syscall : " + str(syscall))
s.info("argv1 : " + str(argv1))
s.info("argv2 : " + argv2)
#syscall = 43
#argv1 = 5382
#argv2 = 'molecule'
sc = '\xeb\x13\x00\x32'
sc += '\x0e\x00\x10\x12' # beq     s0,s0,0x4
sc += '\x00\x00\x00\x32'
sc += '\x19\x00\x00\xea'
sc += '\x22\x00\x00\x14'
argv2str = str(argv2)
argv2_2 = [u16(argv2[:2]),u16(argv2[2:4]),u16(argv2[4:6]),u16(argv2[6:8])]
argv2_1 = u64(argv2)
argv2 = [u32(argv2[:4]), u32(argv2[4:])]
print "---- i386 ----"
print "---- amd64 ----"
context.arch = 'i386'
sc += asm('nop') * 0x5
sc += asm('mov esi, %d' % argv2[0])
sc += asm('mov edi, %d' % argv2[1])
sc += asm('mov DWORD PTR [esp-0x4],edi')
sc += asm('mov DWORD PTR [esp-0x8],esi')
sc += asm('sub esp, 0x8')
sc += asm('mov ecx, esp')
sc += asm('mov ebx, %d' % argv1)
sc += asm('mov esi, ecx')
sc += asm('mov edi, ebx')
sc += asm('mov al, %d' % syscall)
sc += asm('syscall')
print disasm(sc)
print "---- mips ----"
context.arch = 'mips'
sc += 'A' * 0x7
sc += asm('slti $a2, $zero, -1')
sc += asm('slti $a1, $zero, -1')
sc += asm('slti $a0, $zero, -1')
sc += asm('li $t7, %d' % argv2[0])
sc += asm('sw $t7, -12($sp)')
sc += asm('li $t6, %d' % argv2[1])
sc += asm('sw $t6, -8($sp)')
sc += asm('sw $zero, -4($sp)')
sc += asm('li $a0, %d' % argv1)
sc += asm('la $a1, -12($sp)')
sc += asm('li $v0, %d' % syscall)
sc += asm('syscall')
#debug()
print disasm(sc)
#a = gdb.debug_shellcode(sc)
print "---- arm ----"
context.arch = 'arm'
sc += asm('''
add  r3, pc, #1
bx   r3
.thumb
mov  r1, pc
adds r1, #12
mov  r0, #%d
mov  r7, #%d
svc 1
''' % (argv1, syscall))
sc += argv2str
print disasm(sc)
print "---- arm64 ----"
context.arch = 'arm64'
sc += asm('adr x1, 0x10')
sc += asm('mov x0, %d' % argv1)
sc += asm('mov x8, %d' % syscall)
sc += asm('svc #0x0')
sc += argv2str
print disasm(sc)
s.sendline(sc.encode('hex'))
s.interactive()
You submit your code as a hex encoded string of max 4096 characters (2048 bytes)

Your shellcode: Results:
x86: Success
x86-64: Success
ARM: Success
ARM64: Success
MIPS: Success

Congratulations! Here is your flag: midnight{Its_shellz_all_the_w4y_d0wn}