Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
Tags
- 풀이
- Docker
- 취약점
- .net core 7
- level trigger
- cbpf
- epoll_wait
- wrefresh
- edge trigger
- wnourefresh
- architecture
- LOB
- packet flow
- .nret core 배포
- rfc5508
- ioctl
- ncurses
- C언어
- packet filter
- REDIS
- epoll
- iptables
- mvwin
- Compiler
- vtable
- 어셈블리어
- doupdate
- DOCKER-USER
- BOF
- NAPT
Archives
- Today
- Total
Tuuna Computer Science
[L.O.B] Load Of BufferOverflow) golem -> darkknight 본문
LOB문제를 풀어보다가 재미난 사실을 알아서 풀이과정을 서술하려함.
일단 문제의 소스코드를 보면 FPO를 사용하라고 나와있다.
FPO는 Frame Pointer Overflow로 SFP 즉, 복구 EBP의 값을 조작해서 해결하려는 것이다.
문제의 소스를 더 자세히 보자.
argv[1]을 problem_child함수의 인자로 보내고 problem_child의 함수에서 buffer배열 40바이트 선언,
strncpy함수로 매개변수를 buffer로 41바이트 복사하고 있다.
여기서 유의할 점은 버퍼의 크기는 40바이트인데 41바이트를 넘기고 있다는 것이다.
즉, 이는 복구 EBP의 값을 오버플로우하여 값을 조작할 수 있음을 읨한다.
일단 FPO를 이해하기 위한 스택구조를 살펴보자.
일단 아래의 그림은 "\x90"*40+"\x00"를 매개변수로 주어지고 problem_child를 호출했을 때의 스택구조이다.
|---------------------------------------------------| <-esp
|main의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------| <- ebp
|main을 호출한 어떠한 함수의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
이래는 problem_child에서 buffer를 선언했을 때의 스택구조이다.
|---------------------------------------------------| <- esp
| |
| buffer 40 byte |
| |
|---------------------------------------------------|
|main의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------| <-ebp
|main을 호출한 어떠한 함수의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
아래는 printf를 호출떄의 스택구조이다. (핵심이다. )
|---------------------------------------------------| <- esp
| printf 포맷 주소 |
|---------------------------------------------------|
| buffer의 주소 |
|---------------------------------------------------|
| |
| buffer 40 byte |
| |
|---------------------------------------------------| <- ebp
|main의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
|main을 호출한 어떠한 함수의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
위 그림이 왜 핵심인지는 좀 이따 설명할것입니다.
problem_child함수에서 leave명령어
이 leave명령어를 쪼개면
mov esp, ebp
pop ebp 로 만들 수 있다. 이를 그대로 진행해 보자.
mov esp, ebp를 진행했을 떄
|---------------------------------------------------|
| printf 포맷 주소 |
|---------------------------------------------------|
| buffer의 주소 |
|---------------------------------------------------|
| |
| buffer 40 byte |
| |
|---------------------------------------------------| <- ebp = esp
|main의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
|main을 호출한 어떠한 함수의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
pop ebp를 진행하였을 때
그림을 먼저보기전에 우리가 "\x90"*40+"\x00"를 주었지 아니한가 그럼 main의 ebp의 값은
기존값에서 끝의 1바이트가 우리가 넣은 "B"의 값으로 대체되어 있을 것이다.
예를 들어 원래의 main의 ebp가 0xbffffaaa라면 "\x00"를 넣어 0xbffffa00로 대체되어 진다는 의미이다.
여기서 우린 "B"의 값 대신 버퍼의 주소의 끝 바이트로 설정하는 것이 어떨까
일단 위 그럼을 통해 알 수 있는 점은 버퍼의 주소가 0xbffffca4라는 점을 알 수 있다.
그럼 ebp의 값의 끝 바이트를 \xa4로 설정하면 될까?
그럼 main의 ebp값의 끝 바이트를 \xa4로 설정해놓고 진행을 해보자.
자 그럼 다시 pop ebp를 진행하였을 때 스택 구조
|---------------------------------------------------|
| printf 포맷 주소 |
|---------------------------------------------------|
| buffer의 주소(인자) |
|---------------------------------------------------| <- ebp
| |
| buffer 40 byte |
| |
|---------------------------------------------------|
|main의 ebp |
|---------------------------------------------------| <- esp
|ret |
|---------------------------------------------------|
|main을 호출한 어떠한 함수의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
여기서 problem_child함수의 ret을 실행하면 이 함수를 호출한 main함수의 다음 주소로 이동한다.
|---------------------------------------------------|
| printf 포맷 주소 |
|---------------------------------------------------|
| buffer의 주소 |
|---------------------------------------------------| <- ebp
| |
| buffer 40 byte |
| |
|---------------------------------------------------|
|main의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------| <-esp
|main을 호출한 어떠한 함수의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
이제 main함수의 leave명령어 중 mov esp, ebp를 실행해 보자.
|---------------------------------------------------|
| printf 포맷 주소 |
|---------------------------------------------------|
| buffer의 주소 |
|---------------------------------------------------| <- ebp = esp
| |
| buffer 40 byte |
| |
|---------------------------------------------------|
|main의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
|main을 호출한 어떠한 함수의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
leave명령 중 pop ebp를 실행해 보자.
(이제 ebp를 신경쓸 필요가 없다.)
|---------------------------------------------------|
| printf 포맷 주소 |
|---------------------------------------------------|
| buffer의 주소 (인자) |
|---------------------------------------------------|
| | <- esp(4바이트 증가)
| buffer 40 byte |
| |
|---------------------------------------------------|
|main의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
|main을 호출한 어떠한 함수의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
이제 main함수의 ret를 실행해보자.
ret은 pop eip와 jmp eip로 이루어져 있다.
pop eip를 진행하였을 때 스택구조
그리고 eip에는 buffer주소에 있는 값 4바이트가 들어간다.
|---------------------------------------------------|
| printf 포맷 주소 |
|---------------------------------------------------|
| buffer의 주소(인자) |
|---------------------------------------------------|
| |
| buffer 40 byte | <- esp (전에 esp보다 4증가함)
| |
|---------------------------------------------------|
|main의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
|main을 호출한 어떠한 함수의 ebp |
|---------------------------------------------------|
|ret |
|---------------------------------------------------|
여기서 문제점이 발생한다. 버퍼에는 우리의 값이 들어 있는데 이 값들은 \x90\x90\x90\x90의 값이라서 eip가 저 주소로 못 뛰어간다.
그래서 우리는 아까 printf의 인자로 buffer의 주소를 push했지 않는가
이를 활용하는 것이다. 즉, 처음 ebp를 버퍼의 주소로 조작하는 것이 아닌 그보다 8보다 적은 수로 조작하는 것이다!
즉, 0xbffffca4가 아닌 0xbffffc9c로 조작해야 한다.
그럼 버퍼의 주소-8한 상황에서 main의 leave를 실행하고 버퍼의주소-4가 된다.
여기서 버퍼의 주소-4에는 printf함수에 준 버퍼의 주소값이 있기 때문에
main의 ret(pop eip)를 했을 때 버퍼의 주소가 eip에 정상적으로 들어가게 된다.
ret(jmp eip)를 통해 eip가버퍼의 주소를 실행하게 된다.
이때 버퍼의 주소에 쉘코드를 넣어주면 끝!
구성된 payload :
./darkknight `python -c 'print "\x90"*15+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"+"\x9c"'`
이 문제의 풀이법은 2가지가 있다 printf가 없음을 대비해서 직접 버퍼의 주소를 적어주는 것과 printf함수의 인자를 활용해서 푸는 것.
문제를 풀다가 후자의 방법이 신기하여 풀이법을 올림. 또한 한번 메모리에 값이 씌워지면 덮을때 까진 계속해서 값이 남아있음을 깨달음 ㅎ
이 풀이법을 통해 가장 하고싶은 말은 메모리에 한 번 씌여진 값은 이 메모리 영역을 어떠한 다른 값으러 덮기전까지 or 컴퓨터를 끄기전까지
값은 지워지지 않음임.
'L.O.B' 카테고리의 다른 글
[L.O.B] Load Of BufferOverflow) assassin->zombie_assassin (0) | 2019.03.30 |
---|---|
[L.O.B] Load Of BufferOverflow) bugbear -> giant (0) | 2019.03.27 |
Comments