안녕하세요. 오랜만에 게시글을 쓰는 것 같습니다.
오늘은 최근에 풀었던 AmateursCTF 2024 - baby-sandbox 문제에서 몰랐던 사실을 알게 되어 관련 글을 포스팅하려 합니다.
해당 문제는 유저 입력 값을 쉘코드로 사용하는 문제입니다. 하지만, 그냥 실행해주지는 않습니다.
보시는 바와 같이 몇 가지 제약 사항들이 존재합니다.
1. 해당 바이너리는 PIE가 설정되어 있습니다. (메모리 주소 랜덤)
2. 쉘코드 영역 주소는 0x1337000로 고정되며 r-x 권한을 가지고 있습니다.
3. 유저 입력 값 중 시스템 콜 명령어가 포함되어 있으면 안 됩니다.
- int 0x80 "CD 80"
- syscall "0F 05"
4. fs, gs 사용 불가능. (fs, gs로 메모리 주소 계산 방지)
5. 모든 레지스터를 0x1337133713371337 으로 초기화
모든 조건 및 제약 사항을 가지고 쉘코드 영역 0x1337000 으로 진입한 시점에는 다음과 같습니다.
[----------------------------------registers-----------------------------------]
RAX: 0x1337000 (lea rax,[rip+0xc8c] # 0x1337c93)
RBX: 0x1337133713371337
RCX: 0x1337133713371337
RDX: 0x1337133713371337
RSI: 0x1337133713371337
RDI: 0x1337133713371337
RBP: 0x1337133713371337
RSP: 0x1337133713371337
RIP: 0x1337000 (lea rax,[rip+0xc8c] # 0x1337c93)
R8 : 0x1337133713371337
R9 : 0x1337133713371337
R10: 0x1337133713371337
R11: 0x1337133713371337
R12: 0x1337133713371337
R13: 0x1337133713371337
R14: 0x1337133713371337
R15: 0x1337133713371337
[-------------------------------------code-------------------------------------]
=> 0x1337000: lea rax,[rip+0xc8c] # 0x1337c93
0x1337007: mov rdi,rax
0x133700a: mov eax,0x0
0x133700f: add BYTE PTR [rax],al
[------------------------------------stack-------------------------------------]
Invalid $SP address: 0x1337133713371337
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0000000001337000 in ?? ()
gdb-peda$ vmmap
Start End Perm Name
0x01337000 0x01338000 r-xp mapped
현재 0x1337000 위치에는 저의 입력 값이 쉘코드로 사용되고 있으며 RBP, RSP 레지스터 또한 0x13371337133713337 로 설정되어 있기 때문에 ROP 체이닝 등 각종 기법들을 사용할 수 없는 상황입니다.
여기서 메모리 릭을 어떻게 할지.. 등 많은 고민을 하다
2023년에 나온 문제의 업그레이드 버전임을 알고, 해당 문제 풀이를 참고할 수 있었습니다.
0x1337000 부터 0x1000 단위로 주소 값을 올려가며 캐시 메모리 액세스 시간차로 접근 가능한 주소를 구하는 방식이었습니다.
GPT-4 모델을 사용하여 좀 더 정확한 설명을 요구한 결과입니다. 참고하기 좋은 것 같습니다.
2023년 롸이트업에서 사용된 코드는 함수 스택 프롤로그/에필로그 과정이 있기 때문에
스택이 오염된 2024년 버전에서는 사용하지 못하였습니다.
그래서 스택 사용을 하지 않는 버전으로 직접 수동으로 어셈을 만들어서 로컬에서 테스트한 결과
실제로 접근 가능한 주소는 캐시에 히트되어 그런지 구분이 가능할 정도로 빨랐으며
이를 기반으로 메인 바이너리가 어느 위치에 매핑되었는지 알아낼 수 있었습니다.
하지만 로컬에서는 쉘이 따지지만, 리모트 서버에서는 쉘이 따지지 않는 상황이 발생했습니다.
여러 가지 상황을 고려했을 때 시간제한이 있고, 일정 시간이 지나면 소켓 연결을 끊어 버리는 것 같았습니다.
메인 바이너리의 주소를 구하기 위해서는 0x1337000 부터 0x1000 씩 올려가며 구하기에는 많은 시간이 소요되었기 때문에, 0x0007ff0000000000 주소에 고정돼서 매핑되는 스택 주소를 사용하여 찾기로 하였고 스택에 있는 메인함수 주소를 구할 수 있었습니다.
주소를 릭 하는 과정을 단축하여 리모트 연결에서도 성공하여 플래그를 얻을 수 있었습니다.
'# technic > - pwnable' 카테고리의 다른 글
Windows용 AFL(American Fuzzer Lop) - winafl (0) | 2018.10.20 |
---|---|
syscall socket x86, x86-x64 (0) | 2018.09.30 |
SCP 명령어로 바이너리를 옮기는 방법 (0) | 2018.09.30 |
Heap Exploitation 번역 (0) | 2017.06.08 |
[ubuntu] qira 설치 에러 (0) | 2016.12.14 |