Off by one
1. Off by one
Off-by-one 은 문자열의 범위 1byte 로 인해 발생하는 취약점이다
문자열의 마지막에는 반드시 NULL 값이 들어간다
→ 배열에 문자열을 저장할 때는 반드시 NULL 값까지 고려해서 저장해야 한다
하지만 정해진 공간을 모두 문자열로 채우면, NULL 값은 SFP 를 침범하게 된다
2. 함수 에필로그
1. leave
함수의 마지막에는 leave 와 ret 명령이 있다
mov esp, ebp
pop ebp
leave 는 위와 같은 명령을 수행한다
leave 에서는 스택 포인터를 함수를 호출하기 이전의 주소로 되돌리는 작업을 한다
스택을 살펴보자
mov esp, ebp 실행 전의 스택 상태는 위와 같다
mov esp, ebp 를 실행하면 esp 가 ebp 가 가리키는 곳으로 이동하게 된다
그 다음으로 pop ebp 를 수행한다
정상적인 상태에서는 esp 에 SFP 값이 위치하게 된다
→ pop ebp 를 수행하면 ebp 에 SFP 값이 들어가고 ebp 가 이동한다
off-by-one 취약점에 의해 SFP 의 1byte 가 변조된 상태에서는
ebp 가 전혀 다른 곳을 가리키게 된다
변조된 SFP 값으로 인해 ebp 는 위와 같이 다른 주소로 이동하게 된다
esp 에는 문제가 없기 때문에 ret 는 문제 없이 실행된다
2. ret
pop eip
jmp eip
ret 는 위와 같은 명령을 수행한다
pop eip 를 수행한 스택은 다음과 같다
eip 에 RET 값을 저장하고 esp 를 이동한다
마지막 jmp eip 명령으로 main 함수로 돌아오게 된다.
3. main leave
mov esp, ebp 실행 전의 스택이다
mov esp, ebp 실행 후의 스택 상태이다
변조된 SFP 를 가리키던 ebp 로 인해 esp 도 잘못된 값을 가리키게 된다
pop ebp 를 수행한다
esp 가 가리키던 부분의 값을 ebp 에 pop 하고 esp 를 이동한다
→ ebp 는 전혀 다른 값을 가리키게 되고, esp 는 ebp 주소에서 4byte 더한 값으로 이동한다
4. main ret
마지막으로 main 함수의 ret 가 수행된다
- pop eip
esp 의 값이 eip 로 pop 된다
- jmp eip
eip 가 가지고 있는 주소로 jmp 하게 된다
esp 는 이상한 값을 가리키고 있기 때문에 이상한 곳으로 jmp 하게 된다
→ shellcode 가 있었다면 쉘 실행이 가능했을 것이다
3. 예제 코드
1. C
// Name : off-by-one.c
// Compile : gcc -o obo off-by-one.c -fno-stack-protector -mpreferred-stack-boundary=2 -z execstack -no-pie -z norelro -m32
#include <stdio.h>
void copy_buf(char *buf) {
char temp[16];
read(0, temp, sizeof(temp));
for(int i = 0; i <= sizeof(temp); i++)
buf[i] = temp[i];
}
int main(void) {
char buf[16] = "";
copy_buf(buf);
return 0;
}
main 에서 크기가 16인 배열을 선언한 후 copy_buf 함수에서 배열을 채우는 코드이다.
for 반복문을 이용하여 배열을 채우게 된다
i 는 최대값이 16 이지만 배열은 마지막 NULL byte 를 제외하면 15 의 크기를 가지게 된다.
따라서 buf 를 넘어 SFP 1byte 를 덮어씌우게 된다
→ off-by-one 취약점 발생
2. 어셈블리
- main 함수
Dump of assembler code for function main:
0x080491ca <+0>: push ebp
0x080491cb <+1>: mov ebp,esp
0x080491cd <+3>: sub esp,0x10
0x080491d0 <+6>: call 0x8049209 <__x86.get_pc_thunk.ax>
0x080491d5 <+11>: add eax,0x2023
0x080491da <+16>: mov DWORD PTR [ebp-0x10],0x0
0x080491e1 <+23>: mov DWORD PTR [ebp-0xc],0x0
0x080491e8 <+30>: mov DWORD PTR [ebp-0x8],0x0
0x080491ef <+37>: mov DWORD PTR [ebp-0x4],0x0
0x080491f6 <+44>: lea eax,[ebp-0x10]
0x080491f9 <+47>: push eax
0x080491fa <+48>: call 0x8049176 <copy_buf>
0x080491ff <+53>: add esp,0x4
0x08049202 <+56>: mov eax,0x0
0x08049207 <+61>: leave
0x08049208 <+62>: ret
End of assembler dump.
- copy_buf 함수
Dump of assembler code for function copy_buf:
0x08049176 <+0>: push ebp
0x08049177 <+1>: mov ebp,esp
0x08049179 <+3>: push ebx
0x0804917a <+4>: sub esp,0x14
0x0804917d <+7>: call 0x8049209 <__x86.get_pc_thunk.ax>
0x08049182 <+12>: add eax,0x2076
0x08049187 <+17>: push 0x10
0x08049189 <+19>: lea edx,[ebp-0x18]
0x0804918c <+22>: push edx
0x0804918d <+23>: push 0x0
0x0804918f <+25>: mov ebx,eax
0x08049191 <+27>: call 0x8049050 <read@plt>
0x08049196 <+32>: add esp,0xc
0x08049199 <+35>: mov DWORD PTR [ebp-0x8],0x0
0x080491a0 <+42>: jmp 0x80491bb <copy_buf+69>
0x080491a2 <+44>: mov edx,DWORD PTR [ebp-0x8]
0x080491a5 <+47>: mov eax,DWORD PTR [ebp+0x8]
0x080491a8 <+50>: add edx,eax
0x080491aa <+52>: lea ecx,[ebp-0x18]
0x080491ad <+55>: mov eax,DWORD PTR [ebp-0x8]
0x080491b0 <+58>: add eax,ecx
0x080491b2 <+60>: movzx eax,BYTE PTR [eax]
0x080491b5 <+63>: mov BYTE PTR [edx],al
0x080491b7 <+65>: add DWORD PTR [ebp-0x8],0x1
0x080491bb <+69>: mov eax,DWORD PTR [ebp-0x8]
0x080491be <+72>: cmp eax,0x10
0x080491c1 <+75>: jbe 0x80491a2 <copy_buf+44>
0x080491c3 <+77>: nop
0x080491c4 <+78>: nop
0x080491c5 <+79>: mov ebx,DWORD PTR [ebp-0x4]
0x080491c8 <+82>: leave
0x080491c9 <+83>: ret
End of assembler dump.
copy_buf 함수에서 off-by-one 취약점이 발생하게 된다
3. 동적디버깅
copy_buf 함수에 break 를 걸고 함수 실행 전과 후의 ebp 값을 비교해보자
copy_buf 호출 전의 ebp 값은 0xf7ffd020 이다
copy_buf 호출 후 main 함수 복귀 후 ebp 를 확인해보자
copy_buf 호출 후 ebp 의 값은 0xf7ffd010 이다
→ off-by-one 취약점으로 ebp 값이 0xf7ffd020 에서 0xf7ffd010 로 바뀐 것을 확인할 수 있다
'Hacking > Pwnable' 카테고리의 다른 글
SROP (x86) (0) | 2025.03.25 |
---|---|
OOB(Out of Bound) (0) | 2025.03.18 |
PIE (0) | 2024.10.13 |
PIC (0) | 2024.10.12 |
가젯 찾는법 (ROPgadget, ropsearch, rp-lin) (0) | 2024.09.18 |