BROP - Blind Return Oriented Programming

Support HackTricks

Basic Information

이 공격의 목표는 취약한 바이너리에 대한 정보 없이 버퍼 오버플로우를 통해 ROP를 악용할 수 있는 능력입니다. 이 공격은 다음 시나리오를 기반으로 합니다:

  • 스택 취약점과 이를 유발하는 방법에 대한 지식.

  • 충돌 후 재시작되는 서버 애플리케이션.

Attack

1. 취약한 오프셋 찾기 서버의 오작동이 감지될 때까지 한 문자를 더 보내기

2. 카나리 무차별 대입 이를 유출하기 위해

3. 스택에서 저장된 RBP 및 RIP 주소를 무차별 대입하여 유출하기

이 프로세스에 대한 더 많은 정보는 여기 (BF Forked & Threaded Stack Canaries)여기 (BF Addresses in the Stack)에서 찾을 수 있습니다.

4. 정지 가젯 찾기

이 가젯은 기본적으로 ROP 가젯에 의해 흥미로운 무언가가 실행되었음을 확인할 수 있게 해줍니다. 실행이 충돌하지 않았기 때문입니다. 일반적으로 이 가젯은 실행을 중지하는 무언가가 될 것이며, 특정 ROP 가젯이 실행되었음을 확인하기 위해 ROP 체인의 끝에 위치합니다.

5. BROP 가젯 찾기

이 기술은 ret2csu 가젯을 사용합니다. 이는 이 가젯에 일부 명령어 중간에 접근하면 **rsi**와 **rdi**를 제어할 수 있는 가젯을 얻기 때문입니다:

이것들이 가젯입니다:

  • pop rsi; pop r15; ret

  • pop rdi; ret

이 가젯을 사용하면 함수 호출의 2개의 인자를 제어할 수 있다는 점에 주목하세요.

또한, ret2csu 가젯은 매우 독특한 서명을 가지고 있습니다. 왜냐하면 스택에서 6개의 레지스터를 팝하기 때문입니다. 따라서 다음과 같은 체인을 보내면:

'A' * offset + canary + rbp + ADDR + 0xdead * 6 + STOP

STOP이 실행되면, 이는 기본적으로 스택에서 6개의 레지스터를 팝하는 주소가 사용되었다는 것을 의미합니다. 또는 사용된 주소가 또한 STOP 주소였다는 것을 의미합니다.

이 마지막 옵션을 제거하기 위해 다음과 같은 새로운 체인이 실행되며, 이전 체인이 6개의 레지스터를 팝했음을 확인하기 위해 STOP 가젯을 실행하지 않아야 합니다:

'A' * offset + canary + rbp + ADDR

ret2csu 가젯의 주소를 알면 rsirdi를 제어할 가젯의 주소를 유추할 수 있습니다.

6. PLT 찾기

PLT 테이블은 0x400000 또는 스택에서 유출된 RIP 주소에서 검색할 수 있습니다(만약 PIE가 사용되고 있다면). 테이블의 항목16B(0x10B)로 구분되어 있으며, 하나의 함수가 호출될 때 서버는 인자가 올바르지 않더라도 충돌하지 않습니다. 또한, PLT + 6B의 주소를 확인해도 충돌하지 않습니다. 이는 첫 번째 코드가 실행되기 때문입니다.

따라서 다음 동작을 확인하여 PLT 테이블을 찾을 수 있습니다:

  • 'A' * offset + canary + rbp + ADDR + STOP -> 충돌 없음

  • 'A' * offset + canary + rbp + (ADDR + 0x6) + STOP -> 충돌 없음

  • 'A' * offset + canary + rbp + (ADDR + 0x10) + STOP -> 충돌 없음

7. strcmp 찾기

strcmp 함수는 비교되는 문자열의 길이를 rdx 레지스터에 설정합니다. **rdx**는 세 번째 인자이며, 나중에 write를 사용하여 프로그램을 유출하기 위해 0보다 커야 합니다.

이제 함수의 첫 두 인자를 제어할 수 있는 사실을 이용하여 PLT에서 **strcmp**의 위치를 찾을 수 있습니다:

  • strcmp(<non read addr>, <non read addr>) -> 충돌

  • strcmp(<non read addr>, <read addr>) -> 충돌

  • strcmp(<read addr>, <non read addr>) -> 충돌

  • strcmp(<read addr>, <read addr>) -> 충돌 없음

이것은 PLT 테이블의 각 항목을 호출하거나 PLT 느린 경로를 사용하여 확인할 수 있습니다. 이는 기본적으로 PLT 테이블의 항목 + 0xb(이는 **dlresolve**를 호출함) 다음에 스택에 탐색하고자 하는 항목 번호(0부터 시작)를 배치하여 모든 PLT 항목을 스캔하는 것입니다:

  • strcmp(<non read addr>, <read addr>) -> 충돌

  • b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP -> 충돌할 것입니다

  • strcmp(<read addr>, <non read addr>) -> 충돌

  • b'A' * offset + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP

  • strcmp(<read addr>, <read addr>) -> 충돌 없음

  • b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP

기억하세요:

  • BROP + 0x7는 **pop RSI; pop R15; ret;**를 가리킵니다.

  • BROP + 0x9는 **pop RDI; ret;**를 가리킵니다.

  • PLT + 0xb는 dl_resolve 호출을 가리킵니다.

strcmp를 찾으면 **rdx**를 0보다 큰 값으로 설정할 수 있습니다.

일반적으로 rdx는 이미 0보다 큰 값을 가지고 있으므로 이 단계는 필요하지 않을 수 있습니다.

8. Write 또는 동등한 것 찾기

마지막으로, 바이너리를 유출하기 위해 데이터를 유출하는 가젯이 필요합니다. 이 시점에서 2개의 인자를 제어하고 rdx를 0보다 크게 설정할 수 있습니다.

이를 위해 악용할 수 있는 일반적인 함수는 3개가 있습니다:

  • puts(data)

  • dprintf(fd, data)

  • write(fd, data, len(data)

그러나 원본 논문에서는 write 함수만 언급하므로 이에 대해 이야기하겠습니다:

현재 문제는 PLT 내에서 write 함수의 위치를 모른다는 것데이터를 소켓으로 전송할 fd 번호를 모른다는 것입니다.

하지만 PLT 테이블의 위치는 알고 있으며, 그 행동을 기반으로 write를 찾을 수 있습니다. 그리고 우리는 서버와 여러 연결을 만들고 높은 FD를 사용하여 우리의 연결 중 하나와 일치하기를 희망할 수 있습니다.

이 함수들을 찾기 위한 행동 서명:

  • 'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP -> 데이터가 출력되면, puts가 발견된 것입니다.

  • 'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP -> 데이터가 출력되면, dprintf가 발견된 것입니다.

  • 'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + (RIP + 0x1) + p64(0x0) + (PLT + 0xb ) + p64(STRCMP ENTRY) + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP -> 데이터가 출력되면, write가 발견된 것입니다.

Automatic Exploitation

References

Support HackTricks

Last updated