BROP - Blind Return Oriented Programming

HackTricks 지원

기본 정보

이 공격의 목표는 취약한 이진 파일에 대한 어떠한 정보도 없이 버퍼 오버플로우를 통해 ROP를 남용하는 것입니다. 이 공격은 다음 시나리오에 기반을 두고 있습니다:

  • 스택 취약점 및 그것을 트리거하는 방법에 대한 지식.

  • 충돌 후 서버 응용 프로그램이 다시 시작되는 서버 응용 프로그램.

공격

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

2. 캐너리 무차별 대입하여 누출하기

3. 저장된 RBP 및 RIP 무차별 대입하여 누출하기

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

4. 중지 가젯 찾기

이 가젯은 기본적으로 ROP 가젯에 의해 실행된 흥미로운 작업이 충돌하지 않았기 때문에 확인할 수 있도록 합니다. 일반적으로 이 가젯은 실행을 중지하고 특정 ROP 가젯이 실행되었음을 확인하기 위해 ROP 가젯을 찾을 때 ROP 체인의 끝에 위치합니다.

5. BROP 가젯 찾기

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

다음은 이러한 가젯들입니다:

  • pop rsi; pop r15; ret

  • pop rdi; ret

이러한 가젯을 사용하면 함수의 2개의 인수를 제어할 수 있음을 주목하세요.

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

'A' * 오프셋 + 캐너리 + rbp + 주소 + 0xdead * 6 + 중지

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

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

'A' * 오프셋 + 캐너리 + rbp + 주소

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

6. PLT 찾기

PLT 테이블은 0x400000 또는 스택에서 누출된 RIP 주소 (PIE가 사용 중인 경우)부터 검색할 수 있습니다. 테이블의 엔트리는 16B(0x10B)로 구분되며 함수가 호출될 때 인수가 올바르지 않아도 서버가 충돌하지 않습니다. 또한, PLT + 6B의 주소를 확인해도 충돌하지 않습니다. 이는 첫 번째로 실행되는 코드이기 때문입니다.

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

  • 'A' * 오프셋 + 캐너리 + rbp + 주소 + 중지 -> 충돌 없음

  • 'A' * 오프셋 + 캐너리 + rbp + (주소 + 0x6) + 중지 -> 충돌 없음

  • 'A' * 오프셋 + 캐너리 + rbp + (주소 + 0x10) + 중지 -> 충돌 없음

7. strcmp 찾기

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

이제 우리는 이제 함수의 처음 2개의 인수를 제어할 수 있기 때문에 **strcmp**의 위치를 PLT에서 찾을 수 있습니다. 이를 통해 다음과 같은 동작을 사용하여 **strcmp**의 위치를 찾을 수 있습니다:

  • strcmp(<읽을 수 없는 주소>, <읽을 수 없는 주소>) -> 충돌

  • strcmp(<읽을 수 없는 주소>, <읽을 수 있는 주소>) -> 충돌

  • strcmp(<읽을 수 있는 주소>, <읽을 수 없는 주소>) -> 충돌

  • strcmp(<읽을 수 있는 주소>, <읽을 수 있는 주소>) -> 충돌 없음

PLT 테이블의 각 엔트리를 호출하거나 PLT slow path를 사용하여 이를 확인할 수 있습니다. 이는 기본적으로 PLT 테이블의 엔트리 + 0xb (즉, **dlresolve**를 호출)를 호출한 다음 스택에서 조사하려는 엔트리 번호 (0부터 시작)를 따릅니다:

  • strcmp(<읽을 수 없는 주소>, <읽을 수 있는 주소>) -> 충돌

  • b'A' * 오프셋 + 캐너리 + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + 중지 -> 충돌 발생

  • strcmp(<읽을 수 있는 주소>, <읽을 수 없는 주소>) -> 충돌

  • b'A' * 오프셋 + 캐너리 + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + 중지

  • strcmp(<읽을 수 있는 주소>, <읽을 수 있는 주소>) -> 충돌 없음

  • b'A' * 오프셋 + 캐너리 + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + 중지

기억하세요:

  • 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가 발견됨

자동 활용

참고 자료

Last updated